From bf58173656b94243df835d29f6d5a7c4467d4d1e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 6 Sep 2023 15:28:02 -0700 Subject: Checkpoint WebRTC Voice --- indra/CMakeLists.txt | 1 + indra/cmake/CMakeLists.txt | 1 + indra/cmake/LLWebRTC.cmake | 1 + indra/cmake/WebRTC.cmake | 43 + indra/llwebrtc/CMakeLists.txt | 55 + indra/llwebrtc/llwebrtc.cpp | 486 ++ indra/llwebrtc/llwebrtc.h | 127 + indra/llwebrtc/llwebrtc_impl.h | 177 + indra/newview/CMakeLists.txt | 6 + indra/newview/app_settings/settings.xml | 2 +- indra/newview/llvieweraudio.cpp | 4 +- indra/newview/llviewerregion.cpp | 1 + indra/newview/llvoiceclient.cpp | 10 +- indra/newview/llvoiceclient.h | 1 + indra/newview/llvoicewebrtc.cpp | 7299 +++++++++++++++++++++++++++++++ indra/newview/llvoicewebrtc.h | 1102 +++++ 16 files changed, 9310 insertions(+), 6 deletions(-) create mode 100644 indra/cmake/LLWebRTC.cmake create mode 100644 indra/cmake/WebRTC.cmake create mode 100644 indra/llwebrtc/CMakeLists.txt create mode 100644 indra/llwebrtc/llwebrtc.cpp create mode 100644 indra/llwebrtc/llwebrtc.h create mode 100644 indra/llwebrtc/llwebrtc_impl.h create mode 100644 indra/newview/llvoicewebrtc.cpp create mode 100644 indra/newview/llvoicewebrtc.h (limited to 'indra') 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/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 1fd83eadff..8a77d0b882 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -63,6 +63,7 @@ set(cmake_SOURCE_FILES ViewerMiscLibs.cmake VisualLeakDetector.cmake LibVLCPlugin.cmake + WebRTC.cmake XmlRpcEpi.cmake xxHash.cmake ZLIBNG.cmake diff --git a/indra/cmake/LLWebRTC.cmake b/indra/cmake/LLWebRTC.cmake new file mode 100644 index 0000000000..913e28c2ff --- /dev/null +++ b/indra/cmake/LLWebRTC.cmake @@ -0,0 +1 @@ +# -*- cmake -*- \ No newline at end of file diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake new file mode 100644 index 0000000000..f8ce9c8104 --- /dev/null +++ b/indra/cmake/WebRTC.cmake @@ -0,0 +1,43 @@ +# -*- cmake -*- +include(CMakeCopyIfDifferent) + +include(Linking) + +include_guard() + +set(WEBRTC_ROOT ${CMAKE_BINARY_DIR}/../../webrtc/src) +file(COPY ${WEBRTC_ROOT}/out/Default/obj/webrtc.lib + DESTINATION ${CMAKE_BINARY_DIR}/packages/lib/release +) +set(WEBRTC_INCLUDE_DIR ${CMAKE_BINARY_DIR}/packages/include/webrtc) +file(MAKE_DIRECTORY ${WEBRTC_INCLUDE_DIR}) + +file(COPY ${WEBRTC_ROOT}/api + ${WEBRTC_ROOT}/media/base + ${WEBRTC_ROOT}/media/engine + ${WEBRTC_ROOT}/rtc_base + ${WEBRTC_ROOT}/pc + ${WEBRTC_ROOT}/p2p + ${WEBRTC_ROOT}/call + ${WEBRTC_ROOT}/media + ${WEBRTC_ROOT}/system_wrappers + ${WEBRTC_ROOT}/common_video + ${WEBRTC_ROOT}/video + ${WEBRTC_ROOT}/common_audio + ${WEBRTC_ROOT}/logging + ${WEBRTC_ROOT}/third_party/abseil-cpp/absl + DESTINATION ${WEBRTC_INCLUDE_DIR} + FILES_MATCHING PATTERN "*.h" +) + +add_library(ll::webrtc STATIC IMPORTED) + +if (LINUX) + target_link_libraries( ll::webrtc INTERFACE ../webrtc/src/obj/Default/webrtc) +elseif (DARWIN) + target_link_libraries( ll::webrtc INTERFACE ../webrtc/src/obj/Default/webrtc) +elseif (WINDOWS) + set_target_properties( ll::webrtc PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/packages/lib/release/webrtc.lib) + target_link_libraries( ll::webrtc INTERFACE ${CMAKE_BINARY_DIR}/packages/lib/release/webrtc.lib) +endif (LINUX) +target_include_directories( ll::webrtc INTERFACE "${WEBRTC_INCLUDE_DIR}") diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt new file mode 100644 index 0000000000..c0e2520a22 --- /dev/null +++ b/indra/llwebrtc/CMakeLists.txt @@ -0,0 +1,55 @@ +# -*- cmake -*- + +# some webrtc headers require C++ 20 +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +set(CMAKE_GENERATOR_TOOLSET "clang_cl_x64") + +include(00-Common) +include(Linking) +include(WebRTC) + +project(llwebrtc) + +message(STATUS "C Compiler executable: ${CMAKE_C_COMPILER}") +message(STATUS "CXX Compiler executable: ${CMAKE_CXX_COMPILER}") +message(STATUS "Linker executable: ${CMAKE_LINKER}") +message(STATUS "SharedLib: ${SHARED_LIB_STAGING_DIR}") + +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) + +target_link_libraries(llwebrtc PRIVATE ll::webrtc + secur32 + winmm + dmoguids + wmcodecdspuuid + msdmo + strmiids + iphlpapi) +target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +set_property(TARGET llwebrtc PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") + +install(TARGETS llwebrtc RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/sharedlibs/RelWithDebInfo") + +# 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..5e71a00b60 --- /dev/null +++ b/indra/llwebrtc/llwebrtc.cpp @@ -0,0 +1,486 @@ +/** + * @file llaccordionctrl.cpp + * @brief Accordion panel 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 + +#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" + +namespace llwebrtc +{ + +void LLWebRTCImpl::init() +{ + mAnswerReceived = false; + rtc::InitializeSSL(); + mTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory(); + + 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(); + + mSignalingThread->PostTask( + [this]() + { + mDeviceModule = webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, + mTaskQueueFactory.get(), + std::unique_ptr(this)); + mDeviceModule->Init(); + updateDevices(); + }); +} + +void LLWebRTCImpl::refreshDevices() +{ + mSignalingThread->PostTask([this]() { updateDevices(); }); +} + +void LLWebRTCImpl::setDevicesObserver(LLWebRTCDevicesObserver *observer) { mVoiceDevicesObserverList.emplace_back(observer); } + +void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer) +{ + std::vector::iterator it = + std::find(mVoiceDevicesObserverList.begin(), mVoiceDevicesObserverList.end(), observer); + if (it != mVoiceDevicesObserverList.end()) + { + mVoiceDevicesObserverList.erase(it); + } +} + +void LLWebRTCImpl::setCaptureDevice(const std::string &id) +{ + mSignalingThread->PostTask( + [this, id]() + { + int16_t captureDeviceCount = mDeviceModule->RecordingDevices(); + for (int16_t index = 0; index < captureDeviceCount; index++) + { + char name[webrtc::kAdmMaxDeviceNameSize]; + char guid[webrtc::kAdmMaxGuidSize]; + mDeviceModule->RecordingDeviceName(index, name, guid); + if (id == guid || id == name) + { + mDeviceModule->SetRecordingDevice(index); + break; + } + } + }); +} + +void LLWebRTCImpl::setRenderDevice(const std::string &id) +{ + mSignalingThread->PostTask( + [this, id]() + { + int16_t renderDeviceCount = mDeviceModule->RecordingDevices(); + for (int16_t index = 0; index < renderDeviceCount; index++) + { + char name[webrtc::kAdmMaxDeviceNameSize]; + char guid[webrtc::kAdmMaxGuidSize]; + mDeviceModule->PlayoutDeviceName(index, name, guid); + if (id == guid || id == name) + { + mDeviceModule->SetPlayoutDevice(index); + break; + } + } + }); +} + +void LLWebRTCImpl::updateDevices() +{ + int16_t renderDeviceCount = mDeviceModule->PlayoutDevices(); + LLWebRTCVoiceDeviceList renderDeviceList; + for (int16_t index = 0; index < renderDeviceCount; index++) + { + char name[webrtc::kAdmMaxDeviceNameSize]; + char guid[webrtc::kAdmMaxGuidSize]; + mDeviceModule->PlayoutDeviceName(index, name, guid); + renderDeviceList.emplace_back(name, guid); + } + for (auto &observer : mVoiceDevicesObserverList) + { + observer->OnRenderDevicesChanged(renderDeviceList); + } + + int16_t captureDeviceCount = mDeviceModule->RecordingDevices(); + LLWebRTCVoiceDeviceList captureDeviceList; + for (int16_t index = 0; index < captureDeviceCount; index++) + { + char name[webrtc::kAdmMaxDeviceNameSize]; + char guid[webrtc::kAdmMaxGuidSize]; + mDeviceModule->RecordingDeviceName(index, name, guid); + captureDeviceList.emplace_back(name, guid); + } + for (auto &observer : mVoiceDevicesObserverList) + { + observer->OnCaptureDevicesChanged(captureDeviceList); + } +} + +void LLWebRTCImpl::setTuningMode(bool enable) +{ + mSignalingThread->PostTask( + [this, enable]() + { + if (enable) + { + mDeviceModule->InitMicrophone(); + mDeviceModule->InitRecording(); + mDeviceModule->StartRecording(); + mDeviceModule->SetMicrophoneMute(false); + } + else + { + mDeviceModule->StopRecording(); + } + }); +} + +double LLWebRTCImpl::getTuningMicrophoneEnergy() { return mTuningEnergy; } + +void LLWebRTCImpl::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) +{ + if (bytes_per_sample != 4) + { + return; + } + + double energy = 0; + const short *samples = (const short *) audio_samples; + for (size_t index = 0; index < num_samples * num_channels; index++) + { + double sample = (static_cast(samples[index]) / (double) 32768); + energy += sample * sample; + } + mTuningEnergy = std::sqrt(energy); +} + +void LLWebRTCImpl::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) +{ +} + +// +// LLWebRTCSignalInterface +// + +void LLWebRTCImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); } + +void LLWebRTCImpl::unsetSignalingObserver(LLWebRTCSignalingObserver *observer) +{ + std::vector::iterator it = + std::find(mSignalingObserverList.begin(), mSignalingObserverList.end(), observer); + if (it != mSignalingObserverList.end()) + { + mSignalingObserverList.erase(it); + } +} + +bool LLWebRTCImpl::initializeConnection() +{ + RTC_DCHECK(!mPeerConnection); + RTC_DCHECK(!mPeerConnectionFactory); + mAnswerReceived = false; + mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(), + mWorkerThread.get(), + mSignalingThread.get(), + nullptr /* default_adm */, + webrtc::CreateBuiltinAudioEncoderFactory(), + webrtc::CreateBuiltinAudioDecoderFactory(), + nullptr /* video_encoder_factory */, + nullptr /* video_decoder_factory */, + nullptr /* audio_mixer */, + nullptr /* audio_processing */); + + if (!mPeerConnectionFactory) + { + shutdownConnection(); + return false; + } + + webrtc::PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; + webrtc::PeerConnectionInterface::IceServer 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); + + 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 + { + shutdownConnection(); + return false; + } + + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); + + cricket::AudioOptions audioOptions; + audioOptions.auto_gain_control = true; + audioOptions.echo_cancellation = true; + audioOptions.noise_suppression = true; + + rtc::scoped_refptr stream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); + rtc::scoped_refptr audio_track( + mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(cricket::AudioOptions()).get())); + audio_track->set_enabled(true); + stream->AddTrack(audio_track); + + mPeerConnection->AddTrack(audio_track, {"SLStream"}); + mPeerConnection->SetLocalDescription(rtc::scoped_refptr(this)); + + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); + + return mPeerConnection != nullptr; +} + +void LLWebRTCImpl::shutdownConnection() +{ + mPeerConnection = nullptr; + mPeerConnectionFactory = nullptr; +} + +void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) +{ + mSignalingThread->PostTask( + [this, sdp]() + { + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); + mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), + rtc::scoped_refptr(this)); + mAnswerReceived = true; + for (auto &observer : mSignalingObserverList) + { + for (auto &candidate : mCachedIceCandidates) + { + LLWebRTCIceCandidate ice_candidate; + ice_candidate.candidate = candidate->candidate().ToString(); + ice_candidate.mline_index = candidate->sdp_mline_index(); + ice_candidate.sdp_mid = candidate->sdp_mid(); + observer->OnIceCandidate(ice_candidate); + } + mCachedIceCandidates.clear(); + if (mPeerConnection->ice_gathering_state() == webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringComplete) + { + for (auto &observer : mSignalingObserverList) + { + observer->OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE); + } + } + } + }); +} + +void LLWebRTCImpl::setMute(bool mute) +{ + mSignalingThread->PostTask( + [this,mute]() + { + auto senders = mPeerConnection->GetSenders(); + + RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " + << senders.size(); + + for (auto& sender : senders) + { + sender->track()->set_enabled(!mute); + } + }); +} + +// +// PeerConnectionObserver implementation. +// + +void LLWebRTCImpl::OnAddTrack(rtc::scoped_refptr receiver, + const std::vector> &streams) +{ + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); +} + +void LLWebRTCImpl::OnRemoveTrack(rtc::scoped_refptr receiver) +{ + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); +} + +void LLWebRTCImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) +{ + LLWebRTCSignalingObserver::IceGatheringState webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; + switch (new_state) + { + case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringNew: + webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; + break; + case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringGathering: + webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_GATHERING; + break; + case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringComplete: + webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE; + break; + default: + RTC_LOG(LS_ERROR) << __FUNCTION__ << " Bad Ice Gathering State" << new_state; + webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; + return; + } + + if (mAnswerReceived) + { + for (auto &observer : mSignalingObserverList) + { + observer->OnIceGatheringState(webrtc_new_state); + } + } +} + +// Called any time the PeerConnectionState changes. +void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState new_state) +{ + RTC_LOG(LS_ERROR) << __FUNCTION__ << " Peer Connection State Change " << new_state; +} + +void LLWebRTCImpl::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) + { + for (auto &observer : mSignalingObserverList) + { + LLWebRTCIceCandidate ice_candidate; + ice_candidate.candidate = candidate->candidate().ToString(); + ice_candidate.mline_index = candidate->sdp_mline_index(); + ice_candidate.sdp_mid = candidate->sdp_mid(); + observer->OnIceCandidate(ice_candidate); + } + } + else + { + mCachedIceCandidates.push_back( + webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); + } +} + +// +// CreateSessionDescriptionObserver implementation. +// +void LLWebRTCImpl::OnSuccess(webrtc::SessionDescriptionInterface *desc) +{ + std::string sdp; + desc->ToString(&sdp); + RTC_LOG(LS_INFO) << sdp; + + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); + for (auto &observer : mSignalingObserverList) + { + observer->OnOfferAvailable(sdp); + } +} + +void LLWebRTCImpl::OnFailure(webrtc::RTCError error) { RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); } + +// +// SetRemoteDescriptionObserverInterface implementation. +// +void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) +{ + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); + if (!error.ok()) + { + RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); + return; + } + for (auto &observer : mSignalingObserverList) + { + observer->OnAudioEstablished(this); + } +} + +// +// SetLocalDescriptionObserverInterface implementation. +// +void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) +{ + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); + if (!error.ok()) + { + RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); + return; + } + auto desc = mPeerConnection->pending_local_description(); + std::string sdp; + desc->ToString(&sdp); + for (auto &observer : mSignalingObserverList) + { + observer->OnOfferAvailable(sdp); + } +} + +rtc::RefCountedObject *gWebRTCImpl = nullptr; +LLWebRTCDeviceInterface *getDeviceInterface() { return gWebRTCImpl; } +LLWebRTCSignalInterface *getSignalingInterface() { return gWebRTCImpl; } + +void init() +{ + gWebRTCImpl = new rtc::RefCountedObject(); + gWebRTCImpl->AddRef(); + gWebRTCImpl->init(); +} +} // namespace llwebrtc \ No newline at end of file diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h new file mode 100644 index 0000000000..121efaab86 --- /dev/null +++ b/indra/llwebrtc/llwebrtc.h @@ -0,0 +1,127 @@ +/** + * @file llaccordionctrl.cpp + * @brief Accordion panel 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$ + */ + +#ifndef LLWEBRTC_H +#define LLWEBRTC_H + +#include +#include + +#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 +{ +LLSYMEXPORT void init(); + +struct LLWebRTCIceCandidate +{ + std::string candidate; + std::string sdp_mid; + int mline_index; +}; + +class LLWebRTCVoiceDevice +{ + public: + std::string display_name; // friendly value for the user + std::string id; // internal value for selection + + LLWebRTCVoiceDevice(const std::string &display_name, const std::string &id) : + display_name(display_name), + id(id) {}; +}; + +typedef std::vector LLWebRTCVoiceDeviceList; + +class LLWebRTCDevicesObserver +{ + public: + virtual void OnRenderDevicesChanged(const LLWebRTCVoiceDeviceList &render_devices) = 0; + virtual void OnCaptureDevicesChanged(const LLWebRTCVoiceDeviceList &capture_devices) = 0; +}; + +class LLWebRTCDeviceInterface +{ + public: + + virtual void refreshDevices() = 0; + + virtual void setCaptureDevice(const std::string& id) = 0; + virtual void setRenderDevice(const std::string& id) = 0; + + virtual void setDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; + virtual void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; + + virtual void setTuningMode(bool enable) = 0; + virtual double getTuningMicrophoneEnergy() = 0; +}; + +class LLWebRTCAudioInterface +{ + public: + virtual void setMute(bool mute) = 0; +}; + +class LLWebRTCSignalingObserver +{ + public: + enum IceGatheringState{ + ICE_GATHERING_NEW, + ICE_GATHERING_GATHERING, + ICE_GATHERING_COMPLETE + }; + virtual void OnIceGatheringState(IceGatheringState state) = 0; + virtual void OnIceCandidate(const LLWebRTCIceCandidate& candidate) = 0; + virtual void OnOfferAvailable(const std::string& sdp) = 0; + virtual void OnAudioEstablished(LLWebRTCAudioInterface *audio_interface) = 0; +}; + +class LLWebRTCSignalInterface +{ + public: + virtual void setSignalingObserver(LLWebRTCSignalingObserver* observer) = 0; + virtual void unsetSignalingObserver(LLWebRTCSignalingObserver* observer) = 0; + + virtual bool initializeConnection() = 0; + virtual void shutdownConnection() = 0; + virtual void AnswerAvailable(const std::string &sdp) = 0; +}; + +LLSYMEXPORT LLWebRTCDeviceInterface* getDeviceInterface(); +LLSYMEXPORT LLWebRTCSignalInterface* getSignalingInterface(); +} + +#endif // LLWEBRTC_H \ No newline at end of file diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h new file mode 100644 index 0000000000..10916e5a25 --- /dev/null +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -0,0 +1,177 @@ +/** + * @file llaccordionctrl.cpp + * @brief Accordion panel 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$ + */ + +#ifndef LLWEBRTC_IMPL_H +#define LLWEBRTC_IMPL_H + +#define LL_MAKEDLL +#define WEBRTC_WIN 1 +#include "llwebrtc.h" +// WebRTC Includes +#ifdef WEBRTC_WIN +#pragma warning(disable : 4996) +#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 LLWebRTCImpl : public LLWebRTCDeviceInterface, + public LLWebRTCSignalInterface, + public LLWebRTCAudioInterface, + public webrtc::AudioDeviceDataObserver, + public webrtc::PeerConnectionObserver, + public webrtc::CreateSessionDescriptionObserver, + public webrtc::SetRemoteDescriptionObserverInterface, + public webrtc::SetLocalDescriptionObserverInterface + +{ + public: + LLWebRTCImpl() : + mTuningEnergy(0.0) + { + } + ~LLWebRTCImpl() {} + + void init(); + + // + // 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; + double getTuningMicrophoneEnergy() override; + + + 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; + + 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; + + // + // LLWebRTCSignalInterface + // + + void setSignalingObserver(LLWebRTCSignalingObserver *observer) override; + void unsetSignalingObserver(LLWebRTCSignalingObserver *observer) override; + bool initializeConnection() override; + void shutdownConnection() override; + void AnswerAvailable(const std::string &sdp) override; + + + // + // LLWebRTCAudioInterface + // + void setMute(bool mute) override; + + // + // PeerConnectionObserver implementation. + // + + void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {} + void OnAddTrack(rtc::scoped_refptr receiver, + const std::vector> &streams) override; + void OnRemoveTrack(rtc::scoped_refptr receiver) override; + void OnDataChannel(rtc::scoped_refptr 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; + + protected: + std::unique_ptr mNetworkThread; + std::unique_ptr mWorkerThread; + std::unique_ptr mSignalingThread; + rtc::scoped_refptr mPeerConnectionFactory; + webrtc::PeerConnectionInterface::RTCConfiguration mConfiguration; + std::unique_ptr mTaskQueueFactory; + + + // Devices + void updateDevices(); + rtc::scoped_refptr mDeviceModule; + std::vector mVoiceDevicesObserverList; + + double mTuningEnergy; + + // signaling + std::vector mSignalingObserverList; + std::vector> mCachedIceCandidates; + bool mAnswerReceived; + + rtc::scoped_refptr mPeerConnection; +}; + +} + +#endif // LLWEBRTC_IMPL_H \ No newline at end of file diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7a70d0b6e6..6d9b0ab2dc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -30,6 +30,7 @@ include(LLKDU) include(LLPhysicsExtensions) include(LLPrimitive) include(LLWindow) +include(LLWebRTC) include(NDOF) include(NVAPI) include(OPENAL) @@ -689,6 +690,7 @@ set(viewer_SOURCE_FILES llvoiceclient.cpp llvoicevisualizer.cpp llvoicevivox.cpp + llvoicewebrtc.cpp llvoinventorylistener.cpp llvopartgroup.cpp llvosky.cpp @@ -1333,6 +1335,7 @@ set(viewer_HEADER_FILES llvoiceclient.h llvoicevisualizer.h llvoicevivox.h + llvoicewebrtc.h llvoinventorylistener.h llvopartgroup.h llvosky.h @@ -1433,6 +1436,7 @@ if (LINUX) endif (LINUX) if (WINDOWS) + list(APPEND viewer_SOURCE_FILES llappviewerwin32.cpp llwindebug.cpp @@ -1724,6 +1728,7 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/openjp2.dll ${SHARED_LIB_STAGING_DIR}/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/uriparser.dll + ${CMAKE_BINARY_DIR}/llwebrtc/Release/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 @@ -1920,6 +1925,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 00b59f9a4d..a91726917d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15128,7 +15128,7 @@ Type String Value - vivox + webrtc WLSkyDetail diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 6a0edbecb1..184b6d8e93 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -480,11 +480,11 @@ void audio_update_volume(bool force_update) if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized"))) { - voice_inst->setMuteMic(true); + //voice_inst->setMuteMic(true); } else { - voice_inst->setMuteMic(false); + //voice_inst->setMuteMic(false); } } } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 452dcdd8fd..84b007eaa4 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/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 68d9f4ffab..294ae0c9ad 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -24,13 +24,13 @@ * $/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" @@ -161,6 +161,10 @@ void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &age { mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance(); } + if (voice_server == "webrtc") + { + mVoiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); + } else { mVoiceModule = NULL; diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index aa67502908..1e8ff21b4b 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -34,6 +34,7 @@ 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" diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp new file mode 100644 index 0000000000..6e68ca7e4f --- /dev/null +++ b/indra/newview/llvoicewebrtc.cpp @@ -0,0 +1,7299 @@ + /** + * @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 +#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 "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" + +#define USE_SESSION_GROUPS 0 +#define VX_NULL_POSITION -2147483648.0 /*The Silence*/ + +extern LLMenuBarGL* gMenuBarView; +extern void handle_voice_morphing_subscribe(); + +namespace { + const F32 VOLUME_SCALE_WEBRTC = 0.01f; + + const F32 SPEAKING_TIMEOUT = 1.f; + + static const std::string VOICE_SERVER_TYPE = "WebRTC"; + + // Don't retry connecting to the daemon more frequently than this: + const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; + const int DAEMON_CONNECT_RETRY_MAX = 3; + + // Don't send positional updates more frequently than this: + const F32 UPDATE_THROTTLE_SECONDS = 0.5f; + + // Timeout for connection to WebRTC + const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; + const F32 CONNECT_DNS_TIMEOUT = 5.0f; + const int CONNECT_RETRY_MAX = 3; + + const F32 LOGIN_ATTEMPT_TIMEOUT = 30.0f; + const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; + const int LOGIN_RETRY_MAX = 3; + + const F32 PROVISION_RETRY_TIMEOUT = 2.0; + const int PROVISION_RETRY_MAX = 5; + + // 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); + + 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 + // 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; + + // How often to check for expired voice fonts in seconds + const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; + // Time of day at which WebRTC expires voice font subscriptions. + // Used to replace the time portion of received expiry timestamps. + static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; + + // Maximum length of capture buffer recordings in seconds. + const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; + + const int ERROR_WebRTC_OBJECT_NOT_FOUND = 1001; + const int ERROR_WebRTC_NOT_LOGGED_IN = 1007; +} + +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 WebRTC 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 WebRTC levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 + return 30 + (int)(volume * 40.0f); + +} + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLWebRTCVoiceClientMuteListObserver : public LLMuteListObserver +{ + /* virtual */ void onChange() { LLWebRTCVoiceClient::getInstance()->muteListChanged();} +}; + + +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; +} + +static LLWebRTCVoiceClientMuteListObserver mutelist_listener; +static bool sMuteListListener_listening = false; + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool LLWebRTCVoiceClient::sShuttingDown = false; +bool LLWebRTCVoiceClient::sConnected = false; +LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr; + +LLWebRTCVoiceClient::LLWebRTCVoiceClient() : + mSessionTerminateRequested(false), + mRelogRequested(false), + mTerminateDaemon(false), + mSpatialJoiningNum(0), + + mTuningMode(false), + mTuningEnergy(0.0f), + mTuningMicVolume(0), + mTuningMicVolumeDirty(true), + mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume + mTuningSpeakerVolumeDirty(true), + mDevicesListUpdated(false), + + mAreaVoiceDisabled(false), + mAudioSession(), // TBD - should be NULL + mAudioSessionChanged(false), + mNextAudioSession(), + + mCurrentParcelLocalID(0), + mConnectorEstablished(false), + mAccountLoggedIn(false), + mNumberOfAliases(0), + mCommandCookie(0), + mLoginRetryCount(0), + + mBuddyListMapPopulated(false), + mBlockRulesListReceived(false), + mAutoAcceptRulesListReceived(false), + + mSpatialCoordsDirty(false), + mIsInitialized(false), + + mMuteMic(false), + mMuteMicDirty(false), + mFriendsListDirty(true), + + mEarLocation(0), + mSpeakerVolumeDirty(true), + mSpeakerMuteDirty(true), + mMicVolume(0), + mMicVolumeDirty(true), + + mVoiceEnabled(false), + mWriteInProgress(false), + + mLipSyncEnabled(false), + + mVoiceFontsReceived(false), + mVoiceFontsNew(false), + mVoiceFontListDirty(false), + + mCaptureBufferMode(false), + mCaptureBufferRecording(false), + mCaptureBufferRecorded(false), + mCaptureBufferPlaying(false), + mShutdownComplete(true), + mPlayRequestCount(0), + + mAvatarNameCacheConnection(), + mIsInTuningMode(false), + mIsInChannel(false), + mIsJoiningSession(false), + mIsWaitingForFonts(false), + mIsLoggingIn(false), + mIsLoggedIn(false), + mIsProcessingChannels(false), + mIsCoroutineActive(false), + mWebRTCPump("WebRTCClientPump"), + mWebRTCDeviceInterface(nullptr), + mWebRTCSignalingInterface(nullptr), + mWebRTCAudioInterface(nullptr) +{ + sShuttingDown = false; + sConnected = false; + sPump = nullptr; + + mSpeakerVolume = scale_speaker_volume(0); + + mVoiceVersion.serverVersion = ""; + mVoiceVersion.serverType = VOICE_SERVER_TYPE; + + // 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 WebRTC 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); +#endif + + + gIdleCallbacks.addFunction(idle, this); +} + +//--------------------------------------------------- + +LLWebRTCVoiceClient::~LLWebRTCVoiceClient() +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + sShuttingDown = true; +} + +//--------------------------------------------------- + +void LLWebRTCVoiceClient::init(LLPumpIO *pump) +{ + // constructor will set up LLVoiceClient::getInstance() + sPump = pump; + +// LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", +// boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, LLWebRTCVoiceClient::getInstance())); + llwebrtc::init(); + + mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); + mWebRTCDeviceInterface->setDevicesObserver(this); + + mWebRTCSignalingInterface = llwebrtc::getSignalingInterface(); + mWebRTCSignalingInterface->setSignalingObserver(this); +} + +void LLWebRTCVoiceClient::terminate() +{ + if (sShuttingDown) + { + return; + } + + // needs to be done manually here since we will not get another pass in + // coroutines... that mechanism is long since gone. + if (mIsLoggedIn) + { + logoutOfWebRTC(false); + } + + if(sConnected) + { + breakVoiceConnection(false); + sConnected = false; + } + else + { + mRelogRequested = false; + } + + sShuttingDown = true; + sPump = NULL; +} + +//--------------------------------------------------- + +void LLWebRTCVoiceClient::cleanUp() +{ + LL_DEBUGS("Voice") << LL_ENDL; + + deleteAllSessions(); + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); + LL_DEBUGS("Voice") << "exiting" << LL_ENDL; +} + +//--------------------------------------------------- + +const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() +{ + return mVoiceVersion; +} + +//--------------------------------------------------- + +void LLWebRTCVoiceClient::updateSettings() +{ + setVoiceEnabled(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); + setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); +} + +///////////////////////////// +// utility functions + +bool LLWebRTCVoiceClient::writeString(const std::string &str) +{ + bool result = false; + LL_DEBUGS("LowVoice") << "sending:\n" << str << LL_ENDL; + + if(sConnected) + { + 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; + + // check return code - sockets will fail (broken, etc.) + err = apr_socket_send( + mSocket->getSocket(), + (const char*)str.data(), + &written); + + if(err == 0 && written == size) + { + // Success. + result = true; + } + else if (err == 0 && written != size) { + // Did a short write, log it for now + LL_WARNS("Voice") << ") short write on socket sending data to WebRTC daemon." << "Sent " << written << "bytes instead of " << size <AnswerAvailable(channel_sdp); + + if(mAccountLoggedIn) + { + // Already logged in. + LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; + + // Don't process another login. + return; + } + else if ( account_name != mAccountName ) + { + LL_WARNS("Voice") << "Mismatched account name! " << account_name + << " instead of " << mAccountName << LL_ENDL; + } + else + { + mAccountPassword = password; + } +} + +void LLWebRTCVoiceClient::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 +// of a coroutine. +// +// + +typedef enum e_voice_control_coro_state +{ + VOICE_STATE_ERROR = -1, + VOICE_STATE_DONE = 0, + VOICE_STATE_TP_WAIT, // entry point + VOICE_STATE_START_DAEMON, + VOICE_STATE_PROVISION_ACCOUNT, + VOICE_STATE_SESSION_PROVISION_WAIT, + VOICE_STATE_START_SESSION, + VOICE_STATE_WAIT_FOR_SESSION_START, + VOICE_STATE_SESSION_RETRY, + VOICE_STATE_SESSION_ESTABLISHED, + VOICE_STATE_WAIT_FOR_CHANNEL, + VOICE_STATE_DISCONNECT, + VOICE_STATE_WAIT_FOR_EXIT, +} EVoiceControlCoroState; + +void LLWebRTCVoiceClient::voiceControlCoro() +{ + int state = 0; + try + { + // state is passed as a reference instead of being + // a member due to unresolved issues with coroutine + // surviving longer than LLWebRTCVoiceClient + voiceControlStateMachine(); + } + 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") << "voiceControlStateMachine crashed in state " << state << LL_ENDL; + throw; + } +} + +void LLWebRTCVoiceClient::voiceControlStateMachine() +{ + if (sShuttingDown) + { + return; + } + + LL_DEBUGS("Voice") << "starting" << LL_ENDL; + mIsCoroutineActive = true; + LLCoros::set_consuming(true); + + U32 retry = 0; + + mVoiceControlState = VOICE_STATE_TP_WAIT; + + do + { + if (sShuttingDown) + { + // WebRTC singleton performed the exit, logged out, + // cleaned sockets, gateway and no longer cares + // about state of coroutine, so just stop + return; + } + + processIceUpdates(); + + switch (mVoiceControlState) + { + case VOICE_STATE_TP_WAIT: + // starting point for voice + if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) + { + LL_DEBUGS("Voice") << "Suspending voiceControlCoro() momentarily for teleport. Tuning: " << mTuningMode << ". Relog: " << mRelogRequested << LL_ENDL; + llcoro::suspendUntilTimeout(1.0); + } + else + { + mVoiceControlState = VOICE_STATE_START_SESSION; + } + break; + + case VOICE_STATE_START_SESSION: + if (establishVoiceConnection()) + { + mVoiceControlState = VOICE_STATE_WAIT_FOR_SESSION_START; + } + else + { + mVoiceControlState = VOICE_STATE_SESSION_RETRY; + } + break; + + case VOICE_STATE_WAIT_FOR_SESSION_START: + llcoro::suspendUntilTimeout(1.0); + if (!mChannelSDP.empty()) + { + mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; + } + break; + + case VOICE_STATE_PROVISION_ACCOUNT: + if (!provisionVoiceAccount()) + { + mVoiceControlState = VOICE_STATE_SESSION_RETRY; + } + else + { + mVoiceControlState = VOICE_STATE_SESSION_PROVISION_WAIT; + } + break; + case VOICE_STATE_SESSION_PROVISION_WAIT: + llcoro::suspendUntilTimeout(1.0); + break; + + + case VOICE_STATE_SESSION_RETRY: + giveUp(); // cleans sockets and session + if (mRelogRequested) + { + // We failed to connect, give it a bit time before retrying. + retry++; + F32 full_delay = llmin(5.f * (F32)retry, 60.f); + F32 current_delay = 0.f; + LL_INFOS("Voice") << "Voice failed to establish session after " << retry + << " tries. Will attempt to reconnect in " << full_delay + << " seconds" << LL_ENDL; + while (current_delay < full_delay && !sShuttingDown) + { + // Assuming that a second has passed is not accurate, + // but we don't need accurancy here, just to make sure + // that some time passed and not to outlive voice itself + current_delay++; + llcoro::suspendUntilTimeout(1.f); + } + mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + } + else + { + mVoiceControlState = VOICE_STATE_DONE; + } + break; + + case VOICE_STATE_SESSION_ESTABLISHED: + { + if (mTuningMode) + { + performMicTuning(); + } + + mVoiceControlState = VOICE_STATE_WAIT_FOR_CHANNEL; + } + break; + + case VOICE_STATE_WAIT_FOR_CHANNEL: + waitForChannel(); // todo: split into more states like login/fonts + mVoiceControlState = VOICE_STATE_DISCONNECT; + break; + + case VOICE_STATE_DISCONNECT: + LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; + endAndDisconnectSession(); + retry = 0; // Connected without issues + mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + break; + + case VOICE_STATE_WAIT_FOR_EXIT: + if (mRelogRequested && mVoiceEnabled) + { + LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; + mVoiceControlState = VOICE_STATE_TP_WAIT; + } + else + { + mVoiceControlState = VOICE_STATE_DONE; + } + break; + + case VOICE_STATE_DONE: + break; + } + } while (mVoiceControlState > 0); + + if (sShuttingDown) + { + // LLWebRTCVoiceClient might be already dead + return; + } + + mIsCoroutineActive = false; + LL_INFOS("Voice") << "exiting" << LL_ENDL; +} + +bool LLWebRTCVoiceClient::endAndDisconnectSession() +{ + LL_DEBUGS("Voice") << LL_ENDL; + + breakVoiceConnection(true); + + return true; +} + +bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) +{ + if (!sShuttingDown && mVoiceEnabled) + { + LL_WARNS("Voice") << "SLVoice terminated " << ll_stream_notation_sd(data) << LL_ENDL; + terminateAudioSession(false); + closeSocket(); + cleanUp(); + LLVoiceClient::getInstance()->setUserPTTState(false); + gAgent.setVoiceConnected(false); + mRelogRequested = true; + } + return false; +} + + +bool LLWebRTCVoiceClient::provisionVoiceAccount() +{ + LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; + + while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) + { + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + // *TODO* Pump a message for wake up. + llcoro::suspend(); + } + + if (sShuttingDown) + { + return false; + } + + std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); + + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; + + LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); + LLSD body; + LLSD jsep; + jsep["type"] = "offer"; + jsep["sdp"] = mChannelSDP; + body["jsep"] = jsep; + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisioned, this, _1), + boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure, this, url, 3, body, _1)); + return true; +} + +void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) +{ + mVoiceControlState = VOICE_STATE_SESSION_ESTABLISHED; + LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); + std::string channelSDP; + if (result.has("jsep") && + result["jsep"].has("type") && + result["jsep"]["type"] == "answer" && + result["jsep"].has("sdp")) + { + channelSDP = result["jsep"]["sdp"]; + } + std::string voiceAccountServerUri; + std::string voiceUserName = gAgent.getID().asString(); + std::string voicePassword = ""; // no password for now. + + LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" + << " user " << (voiceUserName.empty() ? "not set" : "set") << " password " + << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; + + setLoginInfo(voiceUserName, voicePassword, channelSDP); +} + +void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) +{ + if (sShuttingDown) + { + return; + } + if (retries >= 0) + { + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisioned, this, _1), + boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure, this, url, retries - 1, body, _1)); + } + else + { + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + } +} + +bool LLWebRTCVoiceClient::establishVoiceConnection() +{ + LL_INFOS("Voice") << "Ice Gathering voice account." << LL_ENDL; + while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) + { + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + // *TODO* Pump a message for wake up. + llcoro::suspend(); + return false; + } + + if (!mVoiceEnabled && mIsInitialized) + { + LL_WARNS("Voice") << "cannot establish connection; enabled "<initializeConnection(); +} + +bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) +{ + LL_DEBUGS("Voice") << "( wait=" << corowait << ")" << LL_ENDL; + bool retval(true); + + mShutdownComplete = false; + connectorShutdown(); + + if (corowait) + { + LLSD timeoutResult(LLSDMap("connector", "timeout")); + + LLSD result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + + retval = result.has("connector"); + } + else + { + mRelogRequested = false; //stop the control coro + // If we are not doing a corowait then we must sleep until the connector has responded + // otherwise we may very well close the socket too early. +#if LL_WINDOWS + if (!mShutdownComplete) + { + // The situation that brings us here is a call from ::terminate() + // At this point message system is already down so we can't wait for + // the message, yet we need to receive "connector shutdown response". + // Either wait a bit and emulate it or check gMessageSystem for specific message + _sleep(1000); + if (sConnected) + { + sConnected = false; + LLSD WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); + mWebRTCPump.post(WebRTCevent); + } + mShutdownComplete = true; + } +#endif + } + + LL_DEBUGS("Voice") << "closing SLVoice socket" << LL_ENDL; + closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. + cleanUp(); + sConnected = false; + + return retval; +} + +bool LLWebRTCVoiceClient::loginToWebRTC() +{ + + + mRelogRequested = false; + mIsLoggedIn = true; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + + // Set up the mute list observer if it hasn't been set up already. + if ((!sMuteListListener_listening)) + { + LLMuteList::getInstance()->addObserver(&mutelist_listener); + sMuteListListener_listening = true; + } + + // Set the initial state of mic mute, local speaker volume, etc. + sendLocalAudioUpdates(); + mIsLoggingIn = false; + + return true; +} + +void LLWebRTCVoiceClient::logoutOfWebRTC(bool wait) +{ + if (mIsLoggedIn) + { + // Ensure that we'll re-request provisioning before logging in again + mAccountPassword.clear(); + + logoutSendMessage(); + + if (wait) + { + LLSD timeoutResult(LLSDMap("logout", "timeout")); + LLSD result; + + do + { + LL_DEBUGS("Voice") + << "waiting for logout response on " + << mWebRTCPump.getName() + << LL_ENDL; + + result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + + if (sShuttingDown) + { + break; + } + + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + // Don't get confused by prior queued events -- note that it's + // very important that mWebRTCPump is an LLEventMailDrop, which + // does queue events. + } while (! result["logout"]); + } + else + { + LL_DEBUGS("Voice") << "not waiting for logout" << LL_ENDL; + } + + mIsLoggedIn = false; + } +} + +bool LLWebRTCVoiceClient::requestParcelVoiceInfo() +{ + //_INFOS("Voice") << "Requesting voice info for Parcel" << LL_ENDL; + + LLViewerRegion * region = gAgent.getRegion(); + if (region == NULL || !region->capabilitiesReceived()) + { + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL; + return false; + } + + // grab the cap. + std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); + if (url.empty()) + { + // Region dosn't have the cap. Stop probing. + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; + return false; + } + + // update the parcel + checkParcelChanged(true); + + LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << 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); + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD()); + + if (sShuttingDown) + { + return false; + } + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) + { + // if a terminate request has been received, + // bail and go to the stateSessionTerminated + // state. If the cap request is still pending, + // the responder will check to see if we've moved + // to a new session and won't change any state. + LL_DEBUGS("Voice") << "terminate requested " << mSessionTerminateRequested + << " enabled " << mVoiceEnabled + << " initialized " << mIsInitialized + << LL_ENDL; + terminateAudioSession(true); + return false; + } + + if ((!status) || (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))) + { + if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) + { + LL_WARNS("Voice") << "Session terminated." << LL_ENDL; + } + + LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL; + sessionTerminate(); + 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 (LLViewerParcelMgr::getInstance()->allowAgentVoice()) + { + LL_WARNS("Voice") << "No voice credentials" << LL_ENDL; + } + else + { + LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL; + } + } + + // set the spatial channel. If no voice credentials or uri are + // available, then we simply drop out of voice spatially. + return !setSpatialChannel(uri, credentials); +} + +bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) +{ + mIsJoiningSession = true; + + sessionStatePtr_t oldSession = mAudioSession; + + LL_INFOS("Voice") << "Adding or joining voice session " << nextSession->mHandle << LL_ENDL; + + mAudioSession = nextSession; + mAudioSessionChanged = true; + if (!mAudioSession || !mAudioSession->mReconnect) + { + mNextAudioSession.reset(); + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); + + llcoro::suspend(); + + if (sShuttingDown) + { + return false; + } + + LLSD result; + + if (mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) + { + // Notify observers to let them know there is problem with voice + 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) + { + mSpatialJoiningNum++; + } + + if (!mVoiceEnabled && mIsInitialized) + { + LL_DEBUGS("Voice") << "Voice no longer enabled. Exiting" + << " enabled " << mVoiceEnabled + << " initialized " << mIsInitialized + << LL_ENDL; + mIsJoiningSession = false; + // User bailed out during connect -- jump straight to teardown. + terminateAudioSession(true); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + return false; + } + else if (mSessionTerminateRequested) + { + LL_DEBUGS("Voice") << "Terminate requested" << LL_ENDL; + if (mAudioSession && !mAudioSession->mHandle.empty()) + { + // Only allow direct exits from this state in p2p calls (for cancelling an invite). + // Terminating a half-connected session on other types of calls seems to break something in the WebRTC gateway. + if (mAudioSession->mIsP2P) + { + terminateAudioSession(true); + mIsJoiningSession = false; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + return false; + } + } + } + + 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. + // Just flush it all out and start new. + mWebRTCPump.discard(); + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + + return true; +} + +bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) +{ + + if (mAudioSession) + { + LL_INFOS("Voice") << "terminateAudioSession(" << wait << ") Terminating current voice session " << mAudioSession->mHandle << LL_ENDL; + + if (mIsLoggedIn) + { + if (!mAudioSession->mHandle.empty()) + { + +#if RECORD_EVERYTHING + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/WebRTCrecording"); + { + 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; + } + recordingLoopSave(savepath); +#endif + + sessionMediaDisconnectSendMessage(mAudioSession); + + if (wait) + { + LLSD result; + do + { + LLSD timeoutResult(LLSDMap("session", "timeout")); + + result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + + if (sShuttingDown) + { + return false; + } + + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + if (result.has("session")) + { + if (result.has("handle")) + { + if (result["handle"] != mAudioSession->mHandle) + { + LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; + continue; + } + } + + std::string message = result["session"].asString(); + if (message == "removed" || message == "timeout") + break; + } + } while (true); + + } + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "Session " << mAudioSession->mHandle << " already terminated by logout." << LL_ENDL; + } + + sessionStatePtr_t oldSession = mAudioSession; + + mAudioSession.reset(); + // We just notified status observers about this change. Don't do it again. + mAudioSessionChanged = false; + + // The old session may now need to be deleted. + reapSession(oldSession); + } + else + { + LL_WARNS("Voice") << "terminateAudioSession(" << wait << ") with NULL mAudioSession" << LL_ENDL; + } + + 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 + // the region chat. + mSessionTerminateRequested = false; + + bool status=((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !sShuttingDown); + LL_DEBUGS("Voice") << "exiting" + << " VoiceEnabled " << mVoiceEnabled + << " IsInitialized " << mIsInitialized + << " RelogRequested " << mRelogRequested + << " ShuttingDown " << (sShuttingDown ? "TRUE" : "FALSE") + << " returning " << status + << LL_ENDL; + return status; +} + + +typedef enum e_voice_wait_for_channel_state +{ + VOICE_CHANNEL_STATE_LOGIN = 0, // entry point + VOICE_CHANNEL_STATE_CHECK_EFFECTS, + VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, + VOICE_CHANNEL_STATE_PROCESS_CHANNEL, + VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, + VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK, + VOICE_CHANNEL_STATE_LOGOUT, + VOICE_CHANNEL_STATE_RELOG, + VOICE_CHANNEL_STATE_DONE, +} EVoiceWaitForChannelState; + +bool LLWebRTCVoiceClient::waitForChannel() +{ + LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL; + + EVoiceWaitForChannelState state = VOICE_CHANNEL_STATE_LOGIN; + + do + { + if (sShuttingDown) + { + // terminate() forcefully disconects voice, no need for cleanup + return false; + } + + processIceUpdates(); + switch (state) + { + case VOICE_CHANNEL_STATE_LOGIN: + if (!loginToWebRTC()) + { + return false; + } + state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; + break; + + case VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING: + mIsProcessingChannels = true; + llcoro::suspend(); + state = VOICE_CHANNEL_STATE_PROCESS_CHANNEL; + break; + + case VOICE_CHANNEL_STATE_PROCESS_CHANNEL: + if (mTuningMode) + { + performMicTuning(); + } + else if (mCaptureBufferMode) + { + recordingAndPlaybackMode(); + } + else if (checkParcelChanged() || (mNextAudioSession == NULL)) + { + // the parcel is changed, or we have no pending audio sessions, + // so try to request the parcel voice info + // if we have the cap, we move to the appropriate state + requestParcelVoiceInfo(); //suspends for http reply + } + else if (sessionNeedsRelog(mNextAudioSession)) + { + LL_INFOS("Voice") << "Session requesting reprovision and login." << LL_ENDL; + requestRelog(); + break; + } + else if (mNextAudioSession) + { + sessionStatePtr_t joinSession = mNextAudioSession; + mNextAudioSession.reset(); + if (!runSession(joinSession)) //suspends + { + LL_DEBUGS("Voice") << "runSession returned false; leaving inner loop" << LL_ENDL; + break; + } + else + { + LL_DEBUGS("Voice") + << "runSession returned true to inner loop" + << " RelogRequested=" << mRelogRequested + << " VoiceEnabled=" << mVoiceEnabled + << LL_ENDL; + } + } + + state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY; + break; + + case VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY: + if (!mNextAudioSession) + { + llcoro::suspendUntilTimeout(1.0); + } + state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK; + break; + + case VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK: + if (mVoiceEnabled && !mRelogRequested) + { + state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; + break; + } + else + { + mIsProcessingChannels = false; + LL_DEBUGS("Voice") + << "leaving inner waitForChannel loop" + << " RelogRequested=" << mRelogRequested + << " VoiceEnabled=" << mVoiceEnabled + << LL_ENDL; + state = VOICE_CHANNEL_STATE_LOGOUT; + break; + } + + case VOICE_CHANNEL_STATE_LOGOUT: + logoutOfWebRTC(true /*bool wait*/); + if (mRelogRequested) + { + state = VOICE_CHANNEL_STATE_RELOG; + } + else + { + state = VOICE_CHANNEL_STATE_DONE; + } + break; + + case VOICE_CHANNEL_STATE_RELOG: + LL_DEBUGS("Voice") << "Relog Requested, restarting provisioning" << LL_ENDL; + if (!provisionVoiceAccount()) + { + if (sShuttingDown) + { + return false; + } + LL_WARNS("Voice") << "provisioning voice failed; giving up" << LL_ENDL; + giveUp(); + return false; + } + if (mVoiceEnabled && mRelogRequested) + { + state = VOICE_CHANNEL_STATE_LOGIN; + } + else + { + state = VOICE_CHANNEL_STATE_DONE; + } + break; + case VOICE_CHANNEL_STATE_DONE: + LL_DEBUGS("Voice") + << "exiting" + << " RelogRequested=" << mRelogRequested + << " VoiceEnabled=" << mVoiceEnabled + << LL_ENDL; + return !sShuttingDown; + } + } while (true); +} + +bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) +{ + LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL; + + bool joined_session = addAndJoinSession(session); + + if (sShuttingDown) + { + return false; + } + + if (!joined_session) + { + notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + + if (mSessionTerminateRequested) + { + LL_DEBUGS("Voice") << "runSession terminate requested " << LL_ENDL; + terminateAudioSession(true); + } + // 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. + return !mRelogRequested; + } + + notifyParticipantObservers(); + notifyVoiceFontObservers(); + + LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); + + mIsInChannel = true; + mMuteMicDirty = true; + + while (!sShuttingDown + && mVoiceEnabled + && !mSessionTerminateRequested + && !mTuningMode) + { + + if (sShuttingDown) + { + return false; + } + + if (mSessionTerminateRequested) + { + break; + } + + if (mAudioSession && mAudioSession->mParticipantsChanged) + { + mAudioSession->mParticipantsChanged = false; + notifyParticipantObservers(); + } + + if (!inSpatialChannel()) + { + // When in a non-spatial channel, never send positional updates. + mSpatialCoordsDirty = false; + } + else + { + updatePosition(); + + if (checkParcelChanged()) + { + // *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 + // recheck next time around + if (requestParcelVoiceInfo()) // suspends + { // The parcel voice URI has changed.. break out and reconnect. + break; + } + + if (sShuttingDown) + { + return false; + } + } + // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) + enforceTether(); + } + sendPositionAndVolumeUpdate(); + + // Do notifications for expiring Voice Fonts. + if (mVoiceFontExpiryTimer.hasExpired()) + { + expireVoiceFonts(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + } + + // send any requests to adjust mic and speaker settings if they have changed + sendLocalAudioUpdates(); + + mIsInitialized = true; + LLSD result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, UPDATE_THROTTLE_SECONDS, timeoutEvent); + + if (sShuttingDown) + { + return false; + } + + if (!result.has("timeout")) // logging the timeout event spams the log + { + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + } + if (result.has("session")) + { + if (result.has("handle")) + { + if (!mAudioSession) + { + LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while session is not initiated." << LL_ENDL; + continue; + } + if (result["handle"] != mAudioSession->mHandle) + { + LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; + continue; + } + } + + std::string message = result["session"]; + + if (message == "removed") + { + LL_DEBUGS("Voice") << "session removed" << LL_ENDL; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + break; + } + } + else if (result.has("login")) + { + std::string message = result["login"]; + if (message == "account_logout") + { + LL_DEBUGS("Voice") << "logged out" << LL_ENDL; + mIsLoggedIn = false; + mRelogRequested = true; + break; + } + } + } + + if (sShuttingDown) + { + return false; + } + + mIsInChannel = false; + LL_DEBUGS("Voice") << "terminating at end of runSession" << LL_ENDL; + terminateAudioSession(true); + + return true; +} + +void LLWebRTCVoiceClient::recordingAndPlaybackMode() +{ + LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL; + + while (true) + { + LLSD command; + do + { + command = llcoro::suspendUntilEventOn(mWebRTCPump); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(command) << LL_ENDL; + } while (!command.has("recplay")); + + if (command["recplay"].asString() == "quit") + { + mCaptureBufferMode = false; + break; + } + else if (command["recplay"].asString() == "record") + { + voiceRecordBuffer(); + } + else if (command["recplay"].asString() == "playback") + { + voicePlaybackBuffer(); + } + } + + LL_INFOS("Voice") << "Leaving capture/playback mode." << LL_ENDL; + mCaptureBufferRecording = false; + mCaptureBufferRecorded = false; + mCaptureBufferPlaying = false; + + return; +} + +int LLWebRTCVoiceClient::voiceRecordBuffer() +{ + LLSD timeoutResult(LLSDMap("recplay", "stop")); + + LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL; + + LLSD result; + + captureBufferRecordStartSendMessage(); + notifyVoiceFontObservers(); + + do + { + result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + } while (!result.has("recplay")); + + mCaptureBufferRecorded = true; + + captureBufferRecordStopSendMessage(); + mCaptureBufferRecording = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + return true; + /*TODO expand return to move directly into play*/ +} + +int LLWebRTCVoiceClient::voicePlaybackBuffer() +{ + LLSD timeoutResult(LLSDMap("recplay", "stop")); + + LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL; + + LLSD result; + + do + { + captureBufferPlayStartSendMessage(mPreviewVoiceFont); + + // Store the voice font being previewed, so that we know to restart if it changes. + mPreviewVoiceFontLast = mPreviewVoiceFont; + + do + { + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + } while (!result.has("recplay")); + + if (result["recplay"] == "playback") + continue; // restart playback... May be a font change. + + break; + } while (true); + + // Stop playing. + captureBufferPlayStopSendMessage(); + mCaptureBufferPlaying = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + return true; +} + + +bool LLWebRTCVoiceClient::performMicTuning() +{ + LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; + + mIsInTuningMode = false; + + //--------------------------------------------------------------------- + return true; +} + +//========================================================================= + +void LLWebRTCVoiceClient::closeSocket(void) +{ + mSocket.reset(); + sConnected = false; + mConnectorEstablished = false; + mAccountLoggedIn = false; +} + +void LLWebRTCVoiceClient::loginSendMessage() +{ + std::ostringstream stream; + + bool autoPostCrashDumps = gSavedSettings.getBOOL("WebRTCAutoPostCrashDumps"); + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << mAccountName << "" + << "" << mAccountPassword << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "VerifyAnswer" + << "false" + << "0" + << "Application" + << "5" + << (autoPostCrashDumps?"true":"") + << "\n\n\n"; + + LL_INFOS("Voice") << "Attempting voice login" << LL_ENDL; + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::logout() +{ + // Ensure that we'll re-request provisioning before logging in again + mAccountPassword.clear(); + + logoutSendMessage(); +} + +void LLWebRTCVoiceClient::logoutSendMessage() +{ + if(mAccountLoggedIn) + { + LL_INFOS("Voice") << "Attempting voice logout" << LL_ENDL; + std::ostringstream stream; + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + mAccountLoggedIn = false; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::sessionGroupCreateSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "Normal" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI + << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" + << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"Session.Create.1\">" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" << session->mSIPURI << ""; + + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~"; + + if(!session->mHash.empty()) + { + stream + << "" << LLURI::escape(session->mHash, allowed_chars) << "" + << "SHA1UserName"; + } + + stream + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << font_index << "" + << "" << mChannelName << "" + << "\n\n\n"; + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) +{ + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::string password; + if(!session->mHash.empty()) + { + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~" + ; + password = LLURI::escape(session->mHash, allowed_chars); + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mSIPURI << "" + << "" << mChannelName << "" + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << font_index << "" + << "" << password << "" + << "SHA1UserName" + << "\n\n\n" + ; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t &session) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle + << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" + << LL_ENDL; + + session->mMediaConnectInProgress = true; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.MediaConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "" << font_index << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.TextConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionTerminate() +{ + mSessionTerminateRequested = true; +} + +void LLWebRTCVoiceClient::requestRelog() +{ + mSessionTerminateRequested = true; + mRelogRequested = true; +} + + +void LLWebRTCVoiceClient::leaveAudioSession() +{ + if(mAudioSession) + { + LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; + + if(!mAudioSession->mHandle.empty()) + { + +#if RECORD_EVERYTHING + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/WebRTCrecording"); + { + 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; + } + recordingLoopSave(savepath); +#endif + + sessionMediaDisconnectSendMessage(mAudioSession); + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "called with no active session" << LL_ENDL; + } + sessionTerminate(); +} + +void LLWebRTCVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &session) +{ + std::ostringstream stream; + + sessionGroupTerminateSendMessage(session); + return; + /* + LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); + */ +} + +void LLWebRTCVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session) +{ + std::ostringstream stream; + sessionGroupTerminateSendMessage(session); + return; + /* + LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); + */ + +} + +void LLWebRTCVoiceClient::OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList& render_devices) +{ + clearCaptureDevices(); + for (auto &device : render_devices) + { + LLWebRTCVoiceClient::addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); + } + LLWebRTCVoiceClient::setDevicesListUpdated(true); +} + +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) +{ + bool inTuningMode = mIsInTuningMode; + if (inTuningMode) + { + tuningStop(); + } + mWebRTCDeviceInterface->setCaptureDevice(name); + if (inTuningMode) + { + tuningStart(); + } +} +void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) +{ + mDevicesListUpdated = state; +} + +void LLWebRTCVoiceClient::OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) +{ + clearRenderDevices(); + for (auto &device : render_devices) + { + addRenderDevice(LLVoiceDevice(device.display_name, device.id)); + } + 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::tuningRenderStartSendMessage(const std::string& name, bool loop) +{ + mTuningAudioFile = name; + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "" << (loop?"1":"0") << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::tuningRenderStopSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::tuningCaptureStartSendMessage(int loop) +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "-1" + << "" << loop << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::tuningCaptureStopSendMessage() +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); + + mTuningEnergy = 0.0f; +} + +void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mTuningMicVolume) + { + mTuningMicVolume = scaled_volume; + mTuningMicVolumeDirty = true; + } +} + +void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mTuningSpeakerVolume) + { + mTuningSpeakerVolume = scaled_volume; + mTuningSpeakerVolumeDirty = true; + } +} + +float LLWebRTCVoiceClient::tuningGetEnergy(void) +{ + return mWebRTCDeviceInterface->getTuningMicrophoneEnergy(); +} + +bool LLWebRTCVoiceClient::deviceSettingsAvailable() +{ + bool result = true; + + if(mRenderDevices.empty()) + result = false; + + return result; +} +bool LLWebRTCVoiceClient::deviceSettingsUpdated() +{ + bool updated = mDevicesListUpdated; + if (mDevicesListUpdated) + { + // 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; +} + +void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) +{ + if(clearCurrentList) + { + clearCaptureDevices(); + clearRenderDevices(); + } + mWebRTCDeviceInterface->refreshDevices(); +} + +void LLWebRTCVoiceClient::daemonDied() +{ + // The daemon died, so the connection is gone. Reset everything and start over. + LL_WARNS("Voice") << "Connection to WebRTC daemon lost. Resetting state."<< LL_ENDL; + + //TODO: Try to relaunch the daemon +} + +void LLWebRTCVoiceClient::giveUp() +{ + // All has failed. Clean up and stop trying. + LL_WARNS("Voice") << "Terminating Voice Service" << LL_ENDL; + closeSocket(); + cleanUp(); +} + +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]; + F64 npos[3]; + + // The original XML command was sent like this: + /* + << "" + << "" << pos[VX] << "" + << "" << pos[VZ] << "" + << "" << pos[VY] << "" + << "" + << "" + << "" << mAvatarVelocity[VX] << "" + << "" << mAvatarVelocity[VZ] << "" + << "" << mAvatarVelocity[VY] << "" + << "" + << "" + << "" << l.mV[VX] << "" + << "" << u.mV[VX] << "" + << "" << a.mV[VX] << "" + << "" + << "" + << "" << l.mV[VZ] << "" + << "" << u.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VY] << "" + << "" << u.mV [VZ] << "" + << "" << a.mV [VY] << "" + << ""; + */ + +#if 1 + // This was the original transform done when building the XML command + nat[0] = left.mV[VX]; + nat[1] = up.mV[VX]; + nat[2] = at.mV[VX]; + + nup[0] = left.mV[VZ]; + nup[1] = up.mV[VY]; + nup[2] = at.mV[VZ]; + + nl[0] = left.mV[VY]; + nl[1] = up.mV[VZ]; + nl[2] = at.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY]; + +// nvel[0] = vel.mV[VX]; +// nvel[1] = vel.mV[VZ]; +// nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + + // This was the original transform done in the SDK + nat[0] = at.mV[2]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * left.mV[2]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = at.mV[0]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[0]; + + npos[2] = pos.mdV[2] * -1.0; + npos[1] = pos.mdV[1]; + npos[0] = pos.mdV[0]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } +#else + // This is the compose of the two transforms (at least, that's what I'm trying for) + nat[0] = at.mV[VX]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * up.mV[VZ]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = left.mV[VX]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY] * -1.0; + + nvel[0] = vel.mV[VX]; + nvel[1] = vel.mV[VZ]; + nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + +#endif +} + +void LLWebRTCVoiceClient::setHidden(bool hidden) +{ + mHidden = hidden; + + if (mHidden && inSpatialChannel()) + { + // get out of the channel entirely + leaveAudioSession(); + } + else + { + sendPositionAndVolumeUpdate(); + } +} + +void LLWebRTCVoiceClient::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 << "" + << "" << getAudioSessionHandle() << ""; + + stream << ""; + + LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); + +// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; + l = avatarRot.getLeftRow(); + u = avatarRot.getUpRow(); + a = avatarRot.getFwdRow(); + + pos = mAvatarPosition; + vel = mAvatarVelocity; + + // 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) + { + pos.mdV[i] = VX_NULL_POSITION; + } + } + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << "" + ; + + stream << ""; + + stream << ""; + + LLVector3d earPosition; + LLVector3 earVelocity; + LLMatrix3 earRot; + + switch(mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earVelocity = mCameraVelocity; + earRot = mCameraRot; + break; + + case earLocAvatar: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = avatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = mCameraRot; + break; + } + + l = earRot.getLeftRow(); + u = earRot.getUpRow(); + a = earRot.getFwdRow(); + + pos = earPosition; + vel = earVelocity; + + + oldSDKTransform(l, u, a, pos, vel); + + if (mHidden) + { + for (int i=0;i<3;++i) + { + pos.mdV[i] = VX_NULL_POSITION; + } + } + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << "" + ; + + stream << ""; + + stream << "1"; //do not generate responses for update requests + stream << "\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 + if(!p->mIsSelf) + { + // scale from the range 0.0-1.0 to WebRTC volume in the range 0-100 + S32 volume = ll_round(p->mVolume / VOLUME_SCALE_WEBRTC); + bool mute = p->mOnMuteList; + + if(mute) + { + // SetParticipantMuteForMe doesn't work in p2p sessions. + // If we want the user to be muted, set their volume to 0 as well. + // This isn't perfect, but it will at least reduce their volume to a minimum. + volume = 0; + // Mark the current volume level as set to prevent incoming events + // 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 << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << volume << "" + << "\n\n\n"; + + if(!mAudioSession->mIsP2P) + { + // Send a "mute for me" command for the user + // Doesn't work in P2P sessions + stream << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << (mute?"1":"0") << "" + << "Audio" + << "\n\n\n"; + } + } + + p->mVolumeDirty = false; + } + } + } + + std::string update(stream.str()); + if(!update.empty()) + { + LL_DEBUGS("VoiceUpdate") << "sending update " << update << LL_ENDL; + writeString(update); + } + +} + +void LLWebRTCVoiceClient::sendLocalAudioUpdates() +{ + // Check all of the dirty states and then send messages to those needing to be changed. + // Tuningmode hands its own mute settings. + std::ostringstream stream; + + if (mMuteMicDirty && !mTuningMode) + { + mMuteMicDirty = false; + + // Send a local mute command. + + LL_INFOS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << (mMuteMic ? "true" : "false") << "" + << "\n\n\n"; + + } + + if (mSpeakerMuteDirty && !mTuningMode) + { + const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false"); + + mSpeakerMuteDirty = false; + + LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << muteval << "" + << "\n\n\n"; + + } + + if (mSpeakerVolumeDirty) + { + mSpeakerVolumeDirty = false; + + LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << mSpeakerVolume << "" + << "\n\n\n"; + + } + + if (mMicVolumeDirty) + { + mMicVolumeDirty = false; + + LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << mMicVolume << "" + << "\n\n\n"; + } + + + if (!stream.str().empty()) + { + writeString(stream.str()); + } +} + +/** + * 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! + * + * From the WebRTC Docs: + * + * VadAuto: A flag indicating if the automatic VAD is enabled (1) or disabled (0) + * + * VadHangover: The time (in milliseconds) that it takes + * for the VAD to switch back to silence from speech mode after the last speech + * frame has been detected. + * + * VadNoiseFloor: 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 + * long speech classifiable as noise. + * + * 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, + * while 100 is 'least sensitive') + */ +void LLWebRTCVoiceClient::setupVADParams(unsigned int vad_auto, + unsigned int vad_hangover, + unsigned int vad_noise_floor, + unsigned int vad_sensitivity) +{ + std::ostringstream stream; + + LL_INFOS("Voice") << "Setting the automatic VAD to " + << (vad_auto ? "True" : "False") + << " and discrete values to" + << " VadHangover = " << vad_hangover + << ", VadSensitivity = " << vad_sensitivity + << ", VadNoiseFloor = " << vad_noise_floor + << LL_ENDL; + + // Create a request to set the VAD parameters: + stream << "" + << "" << vad_auto << "" + << "" << vad_hangover << "" + << "" << vad_sensitivity << "" + << "" << vad_noise_floor << "" + << "\n\n\n"; + + if (!stream.str().empty()) + { + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::onVADSettingsChange() +{ + // pick up the VAD variables (one of which was changed) + unsigned int vad_auto = gSavedSettings.getU32("WebRTCVadAuto"); + unsigned int vad_hangover = gSavedSettings.getU32("WebRTCVadHangover"); + unsigned int vad_noise_floor = gSavedSettings.getU32("WebRTCVadNoiseFloor"); + unsigned int vad_sensitivity = gSavedSettings.getU32("WebRTCVadSensitivity"); + + // build a VAD params change request and send it to SLVoice + setupVADParams(vad_auto, vad_hangover, vad_noise_floor, vad_sensitivity); +} + +///////////////////////////// +// WebRTC Signaling Handlers +void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +{ + LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; + + if (state != llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) + { + return; + } + mIceCompleted = true; +} + +void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { + mIceCandidates.push_back(candidate); +} + +void LLWebRTCVoiceClient::processIceUpdates() +{ + LL_INFOS("Voice") << "Ice Gathering voice account." << LL_ENDL; + while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) + { + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + // *TODO* Pump a message for wake up. + llcoro::suspend(); + } + + if (sShuttingDown) + { + return; + } + + std::string url = gAgent.getRegionCapability("VoiceSignalingRequest"); + + LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL; + + 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); + + LLSD body; + if (mIceCandidates.size()) + { + LLSD body; + + for (auto &ice_candidate : mIceCandidates) + { + LLSD body_candidate; + body_candidate["sdpMid"] = ice_candidate.sdp_mid; + body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; + body_candidate["candidate"] = ice_candidate.candidate; + body["candidates"].append(body_candidate); + } + mIceCandidates.clear(); + } + else if (mIceCompleted) + { + LLSD body_candidate; + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + mIceCompleted = false; + } + else + { + return; + } + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, _1), + boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, 3, url, body, _1)); +} + +void LLWebRTCVoiceClient::onIceUpdateComplete(const LLSD& result) +{ + if (sShuttingDown) + { + return; + } +} + +void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result) +{ + if (sShuttingDown) + { + 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." << LL_ENDL; + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, _1), + boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, _1)); + } + else + { + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::OnOfferAvailable(const std::string &sdp) +{ + LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; + mChannelSDP = sdp; +} + +void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * audio_interface) +{ + LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; + mWebRTCAudioInterface = audio_interface; + audio_interface->setMute(true); +} + +///////////////////////////// +// Response/Event handlers + +void LLWebRTCVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) +{ + LLSD result = LLSD::emptyMap(); + + if(statusCode == 0) + { + // Connector created, move forward. + if (connectorHandle == LLWebRTCSecurity::getInstance()->connectorHandle()) + { + LL_INFOS("Voice") << "Voice connector succeeded, WebRTC SDK version is " << versionID << " connector handle " << connectorHandle << LL_ENDL; + mVoiceVersion.serverVersion = versionID; + mConnectorEstablished = true; + mTerminateDaemon = false; + + result["connector"] = LLSD::Boolean(true); + } + else + { + // This shouldn't happen - we are somehow out of sync with SLVoice + // or possibly there are two things trying to run SLVoice at once + // or someone is trying to hack into it. + LL_WARNS("Voice") << "Connector returned wrong handle " + << "(" << connectorHandle << ")" + << " expected (" << LLWebRTCSecurity::getInstance()->connectorHandle() << ")" + << LL_ENDL; + result["connector"] = LLSD::Boolean(false); + // Give up. + mTerminateDaemon = true; + } + } + else if (statusCode == 10028) // web request timeout prior to login + { + // 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 + { + // 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 + { + LL_WARNS("Voice") << "Voice connection failure ("<< statusCode << "): " << statusString << LL_ENDL; + mTerminateDaemon = true; + result["connector"] = LLSD::Boolean(false); + } + + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::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. + LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + result["login"] = LLSD::String("retry"); + } + else if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + result["login"] = LLSD::String("failed"); + } + else + { + // Login succeeded, move forward. + mAccountLoggedIn = true; + mNumberOfAliases = numberOfAliases; + result["login"] = LLSD::String("response_ok"); + } + + mWebRTCPump.post(result); + +} + +void LLWebRTCVoiceClient::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->mErrorStatusString = statusString; + if(session == mAudioSession) + { + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "failed") + ("reason", LLSD::Integer(statusCode))); + + mWebRTCPump.post(WebRTCevent); + } + else + { + reapSession(session); + } + } + } + else + { + LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "created")); + + mWebRTCPump.post(WebRTCevent); + } +} + +void LLWebRTCVoiceClient::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->mErrorStatusString = statusString; + if(session == mAudioSession) + { + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "failed")); + + mWebRTCPump.post(WebRTCevent); + } + else + { + reapSession(session); + } + } + } + else + { + LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "added")); + + mWebRTCPump.post(WebRTCevent); + + } +} + +void LLWebRTCVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) +{ + sessionStatePtr_t session(findSession(requestId)); + // 1026 is session already has media, somehow mediaconnect was called twice on the same session. + // set the session info to reflect that the user is already connected. + if (statusCode == 1026) + { + session->mVoiceActive = true; + session->mMediaConnectInProgress = false; + session->mMediaStreamState = streamStateConnected; + //session->mTextStreamState = streamStateConnected; + session->mErrorStatusCode = 0; + } + else if (statusCode != 0) + { + LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; + if (session) + { + session->mMediaConnectInProgress = false; + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + } + } + else + { + LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::logoutResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; + // Should this ever fail? do we care if it does? + } + LLSD WebRTCevent(LLSDMap("logout", LLSD::Boolean(true))); + + mWebRTCPump.post(WebRTCevent); +} + +void LLWebRTCVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + 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 WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); + + mWebRTCPump.post(WebRTCevent); +} + +void LLWebRTCVoiceClient::sessionAddedEvent( + std::string &uriString, + std::string &alias, + std::string &sessionHandle, + std::string &sessionGroupHandle, + bool isChannel, + bool incoming, + std::string &nameString, + std::string &applicationString) +{ + sessionStatePtr_t session; + + LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; + + session = addSession(uriString, sessionHandle); + if(session) + { + session->mGroupHandle = sessionGroupHandle; + 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) + } + 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)); + } + else + { + 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 = 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) + { + // If we got here, we don't have a proper name. Initiate a lookup. + lookupName(session->mCallerID); + } + } + } +} + +void LLWebRTCVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) +{ + LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; + +#if USE_SESSION_GROUPS + if(mMainSessionGroupHandle.empty()) + { + // This is the first (i.e. "main") session group. Save its handle. + mMainSessionGroupHandle = sessionGroupHandle; + } + else + { + LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; + } +#endif +} + +void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) + { + sessionStatePtr_t oldSession = mAudioSession; + + mAudioSession = session; + mAudioSessionChanged = true; + + // The old session may now need to be deleted. + reapSession(oldSession); + } + + // This is the session we're joining. + if(mIsJoiningSession) + { + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) + ("session", "joined")); + + mWebRTCPump.post(WebRTCevent); + + if(!session->mIsChannel) + { + // this is a p2p session. Make sure the other end is added as a participant. + participantStatePtr_t participant(session->addParticipant(session->mSIPURI)); + if(participant) + { + if(participant->mAvatarIDValid) + { + lookupName(participant->mAvatarID); + } + else if(!session->mName.empty()) + { + 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 + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + } + } + } +} + +void LLWebRTCVoiceClient::sessionRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle) +{ + LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; + + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + leftAudioSession(session); + + // 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); + } + else + { + // Already reaped this session. + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; + } + +} + +void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) +{ + if(session) + { + + if(session->mCreateInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; + } + else if(session->mMediaConnectInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; + } + else if(session == mAudioSession) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; + } + else if(session == mNextAudioSession) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; + } + else + { + // 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 + { +// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; + } +} + +// Returns true if the session seems to indicate we've moved to a region on a different voice server +bool LLWebRTCVoiceClient::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); + } + } + } + + return result; +} + +void LLWebRTCVoiceClient::leftAudioSession(const sessionStatePtr_t &session) +{ + if (mAudioSession == session) + { + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) + ("session", "removed")); + + mWebRTCPump.post(WebRTCevent); + } +} + +void LLWebRTCVoiceClient::accountLoginStateChangeEvent( + std::string &accountHandle, + int statusCode, + std::string &statusString, + int state) +{ + LLSD levent = LLSD::emptyMap(); + + /* + According to Mike S., status codes for this event are: + login_state_logged_out=0, + login_state_logged_in = 1, + login_state_logging_in = 2, + login_state_logging_out = 3, + login_state_resetting = 4, + login_state_error=100 + */ + + LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; + switch(state) + { + case 1: + levent["login"] = LLSD::String("account_login"); + + mWebRTCPump.post(levent); + break; + case 2: + break; + + case 3: + levent["login"] = LLSD::String("account_loggingOut"); + + mWebRTCPump.post(levent); + break; + + case 4: + break; + + case 100: + LL_WARNS("Voice") << "account state event error" << LL_ENDL; + break; + + case 0: + levent["login"] = LLSD::String("account_logout"); + + mWebRTCPump.post(levent); + break; + + default: + //Used to be a commented out warning + LL_WARNS("Voice") << "unknown account state event: " << state << LL_ENDL; + break; + } +} + +void LLWebRTCVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) +{ + LLSD result; + + if (mediaCompletionType == "AuxBufferAudioCapture") + { + mCaptureBufferRecording = false; + result["recplay"] = "end"; + } + else if (mediaCompletionType == "AuxBufferAudioRender") + { + // Ignore all but the last stop event + if (--mPlayRequestCount <= 0) + { + mCaptureBufferPlaying = false; + result["recplay"] = "end"; +// result["recplay"] = "done"; + } + } + else + { + LL_WARNS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; + } + + if (!result.isUndefined()) + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::mediaStreamUpdatedEvent( + 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: + case HTTP_OK: + // generic success + // Don't change the saved error code (it may have been set elsewhere) + break; + default: + // save the status code for later + session->mErrorStatusCode = statusCode; + break; + } + + switch(state) + { + case streamStateDisconnecting: + case streamStateIdle: + // Standard "left audio session", WebRTC state 'disconnected' + session->mVoiceActive = false; + session->mMediaConnectInProgress = false; + leftAudioSession(session); + break; + + case streamStateConnected: + session->mVoiceActive = true; + session->mMediaConnectInProgress = false; + joinedAudioSession(session); + case streamStateConnecting: // do nothing, but prevents a warning getting into the logs. + break; + + case streamStateRinging: + if(incoming) + { + // Send the voice chat invite to the GUI layer + // TODO: Question: Should we correlate with the mute list here? + session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); + session->mVoiceInvitePending = true; + if(session->mName.empty()) + { + lookupName(session->mCallerID); + } + else + { + // Act like we just finished resolving the name + avatarNameResolved(session->mCallerID, session->mName); + } + } + break; + + default: + LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; + break; + + } + + } + else + { + // session disconnectintg and disconnected events arriving after we have already left the session. + LL_DEBUGS("Voice") << "session " << sessionHandle << " not found"<< LL_ENDL; + } +} + +void LLWebRTCVoiceClient::participantAddedEvent( + 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)); + if(session) + { + participantStatePtr_t participant(session->addParticipant(uriString)); + if(participant) + { + participant->mAccountName = nameString; + + LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + + if(participant->mAvatarIDValid) + { + // Initiate a lookup + lookupName(participant->mAvatarID); + } + else + { + // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. + std::string namePortion = displayNameString; + + if(namePortion.empty()) + { + // 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); + } + } + } +} + +void LLWebRTCVoiceClient::participantRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + std::string &nameString) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + participantStatePtr_t participant(session->findParticipant(uriString)); + if(participant) + { + session->removeParticipant(participant); + } + else + { + LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; + } + } + else + { + // a late arriving event on a session we have already left. + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + + +void LLWebRTCVoiceClient::participantUpdatedEvent( + 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; + + participant->mIsSpeaking = isSpeaking; + participant->mIsModeratorMuted = isModeratorMuted; + + // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false + if (isSpeaking) + { + participant->mSpeakingTimeout.reset(); + participant->mPower = energy; + } + else + { + participant->mPower = 0.0f; + } + + // Ignore incoming volume level if it has been explicitly set, or there + // is a volume or mute change pending. + if ( !participant->mVolumeSet && !participant->mVolumeDirty) + { + participant->mVolume = (F32)volume * VOLUME_SCALE_WEBRTC; + } + + // *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 + if (voice_cnl && voice_cnl->getSessionID().notNull()) + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); + if (speaker_manager) + { + speaker_manager->update(true); + + // also initialize voice moderate_mode depend on Agent's participant. See EXT-6937. + // *TODO: remove once a way to request the current voice channel moderation mode is implemented. + if (gAgent.getID() == participant->mAvatarID) + { + speaker_manager->initVoiceModerateMode(); + } + } + } + } + else + { + LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::messageEvent( + std::string &sessionHandle, + std::string &uriString, + std::string &alias, + std::string &messageHeader, + std::string &messageBody, + std::string &applicationString) +{ + LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; +// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; + + LL_INFOS("Voice") << "WebRTC raw message:" << std::endl << messageBody << LL_ENDL; + + if(messageHeader.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos) + { + std::string message; + + { + const std::string startMarker = ", try looking for a 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) + { + // Strip out the tag + message.erase(start, (end + 1) - start); + } + else + { + // Avoid an infinite loop + break; + } + } + } + + // Decode ampersand-escaped chars + { + std::string::size_type mark = 0; + + // The text may contain text encoded with <, >, and & + mark = 0; + while((mark = message.find("<", mark)) != std::string::npos) + { + message.replace(mark, 4, "<"); + mark += 1; + } + + mark = 0; + while((mark = message.find(">", mark)) != std::string::npos) + { + message.replace(mark, 4, ">"); + mark += 1; + } + + mark = 0; + while((mark = message.find("&", mark)) != std::string::npos) + { + message.replace(mark, 5, "&"); + 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) + { + bool is_do_not_disturb = gAgent.isDoNotDisturb(); + bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); + bool is_linden = LLMuteList::isLinden(session->mName); + LLChat chat; + + chat.mMuted = is_muted && !is_linden; + + if(!chat.mMuted) + { + chat.mFromID = session->mCallerID; + chat.mFromName = session->mName; + chat.mSourceType = CHAT_SOURCE_AGENT; + + if(is_do_not_disturb && !is_linden) + { + // 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, + session->mName.c_str(), + message.c_str(), + false, + LLStringUtil::null, // default arg + IM_NOTHING_SPECIAL, // default arg + 0, // default arg + LLUUID::null, // default arg + LLVector3::zero); // default arg + } + } + } +} + +void LLWebRTCVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + + if(session) + { + participantStatePtr_t participant(session->findParticipant(uriString)); + if(participant) + { + if (!stricmp(notificationType.c_str(), "Typing")) + { + // Other end started typing + // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). + // It requires some info for the message, which we don't have here. + } + else if (!stricmp(notificationType.c_str(), "NotTyping")) + { + // Other end stopped typing + // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). + // It requires some info for the message, which we don't have here. + } + else + { + LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id) +{ + // We don't generally need to process this. However, one occurence is when we first connect, and so it is the + // earliest opportunity to learn what we're connected to. + if (statusCode) + { + LL_WARNS("Voice") << "VoiceServiceConnectionStateChangedEvent statusCode: " << statusCode << + "statusString: " << statusString << LL_ENDL; + return; + } + if (build_id.empty()) + { + return; + } + mVoiceVersion.mBuildVersion = build_id; +} + +void LLWebRTCVoiceClient::auxAudioPropertiesEvent(F32 energy) +{ + LL_DEBUGS("VoiceEnergy") << "got energy " << energy << LL_ENDL; + mTuningEnergy = energy; +} + +void LLWebRTCVoiceClient::muteListChanged() +{ + // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. + 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; + } + } +} + +///////////////////////////// +// Managing list of participants +LLWebRTCVoiceClient::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), + mVolumeSet(false), + mVolumeDirty(false), + mAvatarIDValid(false), + mIsSelf(false) +{ +} + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const std::string &uri) +{ + participantStatePtr_t result; + bool useAlternateURI = false; + + // Note: this is mostly the body of LLWebRTCVoiceClient::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. + { + participantMap::iterator iter = mParticipantsByURI.find(uri); + + if(iter == mParticipantsByURI.end()) + { + if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) + { + // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. + // Use mSIPURI instead, since it will be properly encoded. + iter = mParticipantsByURI.find(mSIPURI); + useAlternateURI = true; + } + } + + if(iter != mParticipantsByURI.end()) + { + result = iter->second; + } + } + + if(!result) + { + // participant 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; + if(LLWebRTCVoiceClient::getInstance()->IDFromName(result->mURI, id)) + { + result->mAvatarIDValid = true; + result->mAvatarID = id; + } + else + { + // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. + // This indicates that the ID will not be in the name cache. + 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)) + { + result->mVolumeDirty = true; + mVolumeDirty = true; + } + + LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; + } + + return result; +} + +bool LLWebRTCVoiceClient::participantState::updateMuteState() +{ + bool result = false; + + bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); + if(mOnMuteList != isMuted) + { + mOnMuteList = isMuted; + mVolumeDirty = true; + result = true; + } + return result; +} + +bool LLWebRTCVoiceClient::participantState::isAvatar() +{ + return mAvatarIDValid; +} + +void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant) +{ + if(participant) + { + 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; + } + else if(iter2 == mParticipantsByUUID.end()) + { + LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; + } + else if(iter->second != iter2->second) + { + LL_WARNS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; + } + else + { + mParticipantsByURI.erase(iter); + mParticipantsByUUID.erase(iter2); + + mParticipantsChanged = true; + } + } +} + +void LLWebRTCVoiceClient::sessionState::removeAllParticipants() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + + while(!mParticipantsByURI.empty()) + { + removeParticipant(mParticipantsByURI.begin()->second); + } + + if(!mParticipantsByUUID.empty()) + { + LL_WARNS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; + } +} + +/*static*/ +void LLWebRTCVoiceClient::sessionState::VerifySessions() +{ + std::set::iterator it = mSession.begin(); + while (it != mSession.end()) + { + if ((*it).expired()) + { + LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; + it = mSession.erase(it); + } + else + ++it; + } +} + + +void LLWebRTCVoiceClient::getParticipantList(std::set &participants) +{ + if(mAudioSession) + { + for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); + iter != mAudioSession->mParticipantsByUUID.end(); + iter++) + { + participants.insert(iter->first); + } + } +} + +bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) +{ + if(mAudioSession) + { + return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); + } + return false; +} + + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipant(const std::string &uri) +{ + participantStatePtr_t result; + + participantMap::iterator iter = mParticipantsByURI.find(uri); + + if(iter == mParticipantsByURI.end()) + { + if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) + { + // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. + // Look up the other URI + iter = mParticipantsByURI.find(mSIPURI); + } + } + + if(iter != mParticipantsByURI.end()) + { + result = iter->second; + } + + return result; +} + +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; +} + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const LLUUID& id) +{ + participantStatePtr_t result; + + if(mAudioSession) + { + result = mAudioSession->findParticipantByID(id); + } + + return result; +} + + + +// Check for parcel boundary crossing +bool LLWebRTCVoiceClient::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. + // 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()) + { + if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) + { + // We have changed parcels. Initiate a parcel channel lookup. + if (update) + { + mCurrentParcelLocalID = parcelLocalID; + mCurrentRegionName = regionName; + } + return true; + } + } + } + return false; +} + +bool LLWebRTCVoiceClient::switchChannel( + std::string uri, + bool spatial, + bool no_reconnect, + bool is_p2p, + std::string hash) +{ + bool needsSwitch = !mIsInChannel; + + if (mIsInChannel) + { + if (mSessionTerminateRequested) + { + // 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) + needsSwitch = true; + } + else + { + // mNextAudioSession is null -- this probably means we're on our way back to spatial. + if(!uri.empty()) + { + // We do want to process a switch in this case. + needsSwitch = true; + } + } + } + else + { + // Otherwise, compare against the URI we're in now. + if(mAudioSession) + { + if(mAudioSession->mSIPURI != uri) + { + needsSwitch = true; + } + } + else + { + if(!uri.empty()) + { + // mAudioSession is null -- it's not clear what case would cause this. + // For now, log it as a warning and see if it ever crops up. + LL_WARNS("Voice") << "No current audio session... Forcing switch" << LL_ENDL; + needsSwitch = true; + } + } + } + } + + if(needsSwitch) + { + if(uri.empty()) + { + // Leave any channel we may be in + LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + + sessionStatePtr_t oldSession = mNextAudioSession; + mNextAudioSession.reset(); + + // The old session may now need to be deleted. + reapSession(oldSession); + + // If voice was on, turn it off + if (LLVoiceClient::getInstance()->getUserPTTState()) + { + LLVoiceClient::getInstance()->setUserPTTState(false); + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + } + else + { + LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; + + mNextAudioSession = addSession(uri); + mNextAudioSession->mHash = hash; + mNextAudioSession->mIsSpatial = spatial; + mNextAudioSession->mReconnect = !no_reconnect; + mNextAudioSession->mIsP2P = is_p2p; + } + + if (mIsInChannel) + { + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + } + } + + return needsSwitch; +} + +void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) +{ + mNextAudioSession = session; + + if (mIsInChannel) + { + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + } +} + +void LLWebRTCVoiceClient::setNonSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + switchChannel(uri, false, false, false, credentials); +} + +bool LLWebRTCVoiceClient::setSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + mSpatialSessionURI = uri; + mSpatialSessionCredentials = credentials; + mAreaVoiceDisabled = mSpatialSessionURI.empty(); + + 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. + LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; + return false; + } + else + { + return switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + } +} + +void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) +{ + std::string userURI = sipURIFromID(uuid); + + switchChannel(userURI, false, true, true); +} + +#if 0 +// WebRTC text IMs are not in use. +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::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); + + 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 LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) +{ +#if 0 + // WebRTC 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 +} +bool LLWebRTCVoiceClient::isValidChannel(std::string &sessionHandle) +{ + return(findSession(sessionHandle) != NULL); + +} +bool LLWebRTCVoiceClient::answerInvite(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; +} + +bool LLWebRTCVoiceClient::isVoiceWorking() const +{ + + //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) + // Condition with joining spatial num was added to take into account possible problems with connection to voice + // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. + return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && mIsProcessingChannels; +// return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); +} + +// Returns true if the indicated participant in the current audio session is really an SL avatar. +// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. +BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) +{ + 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. + if(session->mSynthesizedCallerID) + result = FALSE; + } + else + { + // Didn't find a matching session -- check the current audio session for a matching participant + if(mAudioSession) + { + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->isAvatar(); + } + } + } + + 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. +BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) +{ + BOOL result = TRUE; + sessionStatePtr_t session(findSession(session_id)); + + if(session != NULL) + { + result = session->isCallBackPossible(); + } + + return result; +} + +// Returns true if the session can accept text IM's. +// Currently this will be false only for PSTN P2P calls. +BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) +{ + bool result = TRUE; + sessionStatePtr_t session(findSession(session_id)); + + if(session != NULL) + { + result = session->isTextIMPossible(); + } + + return result; +} + + +void LLWebRTCVoiceClient::declineInvite(std::string &sessionHandle) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + sessionMediaDisconnectSendMessage(session); + } +} + +void LLWebRTCVoiceClient::leaveNonSpatialChannel() +{ + LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; + + // 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 LLWebRTCVoiceClient::getCurrentChannel() +{ + std::string result; + + if (mIsInChannel && !mSessionTerminateRequested) + { + result = getAudioSessionURI(); + } + + return result; +} + +bool LLWebRTCVoiceClient::inProximalChannel() +{ + bool result = false; + + if (mIsInChannel && !mSessionTerminateRequested) + { + result = inSpatialChannel(); + } + + return result; +} + +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID &id) +{ + std::string result; + result = "sip:"; + result += nameFromID(id); + result += "@"; + + return result; +} + +std::string LLWebRTCVoiceClient::nameFromAvatar(LLVOAvatar *avatar) +{ + std::string result; + if(avatar) + { + result = nameFromID(avatar->getID()); + } + return result; +} + +std::string LLWebRTCVoiceClient::nameFromID(const LLUUID &uuid) +{ + std::string result; + + if (uuid.isNull()) { + //WebRTC, the uuid emtpy look for the mURIString and return that instead. + //result.assign(uuid.mURIStringName); + LLStringUtil::replaceChar(result, '_', ' '); + return result; + } + // Prepending this apparently prevents conflicts with reserved names inside the WebRTC code. + result = "x"; + + // Base64 encode and replace the pieces of base64 that are less compatible + // with e-mail local-parts. + // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" + result += LLBase64::encode(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 LLWebRTCVoiceClient::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.WebRTC.com" + // If it is, convert to a bare name before doing the transform. + std::string name; + + // Doesn't look like a SIP URI, assume it's an actual name. + if(name.empty()) + name = inName; + + // This will only work if the name is of the proper form. + // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: + // "xFnPP04IpREWNkuw1cOXlhw==" + + if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) + { + // The name appears to have the right form. + + // Reverse the transforms done by nameFromID + std::string temp = name; + LLStringUtil::replaceChar(temp, '-', '+'); + LLStringUtil::replaceChar(temp, '_', '/'); + + U8 rawuuid[UUID_BYTES + 1]; + int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); + if(len == UUID_BYTES) + { + // The decode succeeded. Stuff the bits into the result's UUID + memcpy(uuid.mData, rawuuid, UUID_BYTES); + result = true; + } + } + + if(!result) + { + // WebRTC: not a standard account name, just copy the URI name mURIString field + // and hope for the best. bpj + uuid.setNull(); // WebRTC, set the uuid field to nulls + } + + return result; +} + +std::string LLWebRTCVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) +{ + return avatar->getFullname(); +} + +bool LLWebRTCVoiceClient::inSpatialChannel(void) +{ + bool result = false; + + if(mAudioSession) + { + result = mAudioSession->mIsSpatial; + } + + return result; +} + +std::string LLWebRTCVoiceClient::getAudioSessionURI() +{ + std::string result; + + if(mAudioSession) + result = mAudioSession->mSIPURI; + + return result; +} + +std::string LLWebRTCVoiceClient::getAudioSessionHandle() +{ + std::string result; + + if(mAudioSession) + result = mAudioSession->mHandle; + + return result; +} + + +///////////////////////////// +// Sending updates of current state + +void LLWebRTCVoiceClient::enforceTether(void) +{ + LLVector3d tethered = mCameraRequestedPosition; + + // constrain 'tethered' to within 50m of mAvatarPosition. + { + F32 max_dist = 50.0f; + LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; + F32 camera_distance = (F32)camera_offset.magVec(); + if(camera_distance > max_dist) + { + tethered = mAvatarPosition + + (max_dist / camera_distance) * camera_offset; + } + } + + if(dist_vec_squared(mCameraPosition, tethered) > 0.01) + { + mCameraPosition = tethered; + mSpatialCoordsDirty = true; + } +} + +void LLWebRTCVoiceClient::updatePosition(void) +{ + + LLViewerRegion *region = gAgent.getRegion(); + if(region && isAgentAvatarValid()) + { + 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()); + pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); + + LLWebRTCVoiceClient::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(); + + // TODO: Can we get the head offset from outside the LLVOAvatar? + // pos += LLVector3d(mHeadOffset); + pos += LLVector3d(0.f, 0.f, 1.f); + + LLWebRTCVoiceClient::getInstance()->setAvatarPosition( + pos, // position + LLVector3::zero, // velocity + qrot); // rotation matrix + } +} + +void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ + mCameraRequestedPosition = position; + + if(mCameraVelocity != velocity) + { + mCameraVelocity = velocity; + mSpatialCoordsDirty = true; + } + + if(mCameraRot != rot) + { + mCameraRot = rot; + mSpatialCoordsDirty = true; + } +} + +void 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; + } +} + +bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) +{ + bool result = false; + + if(region) + { + name = region->getName(); + } + + if(!name.empty()) + result = true; + + return result; +} + +void LLWebRTCVoiceClient::leaveChannel(void) +{ + if (mIsInChannel) + { + LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; + mChannelName.clear(); + sessionTerminate(); + } +} + +void LLWebRTCVoiceClient::setMuteMic(bool muted) +{ + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setMute(muted); + } +} + +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; + + if (!mIsCoroutineActive) + { + LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", + boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, 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; + } + + notifyStatusObservers(status); + } + else + { + LL_DEBUGS("Voice") << " no-op" << LL_ENDL; + } +} + +bool LLWebRTCVoiceClient::voiceEnabled() +{ + return gSavedSettings.getBOOL("EnableVoiceChat") && + !gSavedSettings.getBOOL("CmdLineDisableVoice") && + !gNonInteractive; +} + +void LLWebRTCVoiceClient::setLipSyncEnabled(BOOL enabled) +{ + mLipSyncEnabled = enabled; +} + +BOOL LLWebRTCVoiceClient::lipSyncEnabled() +{ + + if ( mVoiceEnabled ) + { + return mLipSyncEnabled; + } + else + { + return FALSE; + } +} + + +void LLWebRTCVoiceClient::setEarLocation(S32 loc) +{ + if(mEarLocation != loc) + { + LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; + + mEarLocation = loc; + mSpatialCoordsDirty = true; + } +} + +void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mSpeakerVolume) + { + int min_volume = scale_speaker_volume(0); + if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) + { + mSpeakerMuteDirty = true; + } + + mSpeakerVolume = scaled_volume; + mSpeakerVolumeDirty = true; + } +} + +void LLWebRTCVoiceClient::setMicGain(F32 volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mMicVolume) + { + mMicVolume = scaled_volume; + mMicVolumeDirty = true; + } +} + +///////////////////////////// +// Accessors for data related to nearby speakers +BOOL LLWebRTCVoiceClient::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 LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) +{ + std::string result; + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mDisplayName; + } + + return result; +} + + + +BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) + { + participant->mIsSpeaking = FALSE; + } + result = participant->mIsSpeaking; + } + + return result; +} + +BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mIsModeratorMuted; + } + + return result; +} + +F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID& id) +{ + F32 result = 0; + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mPower; + } + + return result; +} + +BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + // I'm not sure what the semantics of this should be. + // 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 LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mOnMuteList; + } + + 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(findParticipantByID(id)); + if(participant) + { + result = participant->mVolume; + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. + // LL_DEBUGS("Voice") << "mVolume = " << result << " for " << id << LL_ENDL; + } + + return result; +} + +void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) +{ + if(mAudioSession) + { + participantStatePtr_t participant(findParticipantByID(id)); + if (participant && !participant->mIsSelf) + { + 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 = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); + participant->mVolumeDirty = true; + mAudioSession->mVolumeDirty = true; + } + } +} + +std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) +{ + std::string result; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mGroupID; + } + + return result; +} + +BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() +{ + return mAreaVoiceDisabled; +} + +void LLWebRTCVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; + + if(!mMainSessionGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << deltaFramesPerControlFrame << "" + << "" << "" << "" + << "false" + << "" << seconds << "" + << "\n\n\n"; + + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::recordingLoopSave(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Flush" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::recordingStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::filePlaybackStart(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::filePlaybackStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::filePlaybackSetPaused(bool paused) +{ + // TODO: Implement once WebRTC gives me a sample +} + +void LLWebRTCVoiceClient::filePlaybackSetMode(bool vox, float speed) +{ + // TODO: Implement once WebRTC gives me a sample +} + +//------------------------------------------------------------------------ +std::set LLWebRTCVoiceClient::sessionState::mSession; + + +LLWebRTCVoiceClient::sessionState::sessionState() : + mErrorStatusCode(0), + mMediaStreamState(streamStateUnknown), + mCreateInProgress(false), + mMediaConnectInProgress(false), + mVoiceInvitePending(false), + mTextInvitePending(false), + mSynthesizedCallerID(false), + mIsChannel(false), + mIsSpatial(false), + mIsP2P(false), + mIncoming(false), + mVoiceActive(false), + mReconnect(false), + mVolumeDirty(false), + mMuteDirty(false), + mParticipantsChanged(false) +{ +} + +/*static*/ +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession() +{ + sessionState::ptr_t ptr(new sessionState()); + + std::pair::iterator, bool> result = mSession.insert(ptr); + + if (result.second) + ptr->mMyIterator = result.first; + + return ptr; +} + +LLWebRTCVoiceClient::sessionState::~sessionState() +{ + LL_INFOS("Voice") << "Destroying session handle=" << mHandle << " SIP=" << mSIPURI << LL_ENDL; + if (mMyIterator != mSession.end()) + mSession.erase(mMyIterator); + + removeAllParticipants(); +} + +bool LLWebRTCVoiceClient::sessionState::isCallBackPossible() +{ + // This may change to be explicitly specified by WebRTC in the future... + // Currently, only PSTN P2P calls cannot be returned. + // Conveniently, this is also the only case where we synthesize a caller UUID. + return !mSynthesizedCallerID; +} + +bool LLWebRTCVoiceClient::sessionState::isTextIMPossible() +{ + // This may change to be explicitly specified by WebRTC in the future... + return !mSynthesizedCallerID; +} + + +/*static*/ +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByHandle(const std::string &handle) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByHandle, _1, handle)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchCreatingSessionByURI(const std::string &uri) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCreatingURI, _1, uri)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByURI(const std::string &uri) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testBySIPOrAlterateURI, _1, uri)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByParticipant(const LLUUID &participant_id) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCallerId, _1, participant_id)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +void LLWebRTCVoiceClient::sessionState::for_each(sessionFunc_t func) +{ + std::for_each(mSession.begin(), mSession.end(), boost::bind(for_eachPredicate, _1, func)); +} + +// simple test predicates. +// *TODO: These should be made into lambdas when we can pull the trigger on newer C++ features. +bool LLWebRTCVoiceClient::sessionState::testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle) +{ + ptr_t aLock(a.lock()); + + return aLock ? aLock->mHandle == handle : false; +} + +bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) +{ + ptr_t aLock(a.lock()); + + return aLock ? (aLock->mCreateInProgress && (aLock->mSIPURI == uri)) : false; +} + +bool LLWebRTCVoiceClient::sessionState::testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) +{ + ptr_t aLock(a.lock()); + + return aLock ? ((aLock->mSIPURI == uri) || (aLock->mAlternateSIPURI == uri)) : false; +} + + +bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId) +{ + ptr_t aLock(a.lock()); + + return aLock ? ((aLock->mCallerID == participantId) || (aLock->mIMSessionID == participantId)) : false; +} + +/*static*/ +void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceClient::sessionState::wptr_t &a, sessionFunc_t func) +{ + ptr_t aLock(a.lock()); + + if (aLock) + func(aLock); + else + { + LL_WARNS("Voice") << "Stale handle in session map!" << LL_ENDL; + } +} + + + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const std::string &handle) +{ + sessionStatePtr_t result; + sessionMap::iterator iter = mSessionsByHandle.find(handle); + if(iter != mSessionsByHandle.end()) + { + result = iter->second; + } + + return result; +} + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) +{ + sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri); + + return result; +} + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const LLUUID &participant_id) +{ + sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); + + return result; +} + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &uri, const std::string &handle) +{ + sessionStatePtr_t result; + + if(handle.empty()) + { + // No handle supplied. + // Check whether there's already a session with this URI + result = sessionState::matchSessionByURI(uri); + } + else // (!handle.empty()) + { + // Check for an existing session with this handle + sessionMap::iterator iter = mSessionsByHandle.find(handle); + + if(iter != mSessionsByHandle.end()) + { + result = iter->second; + } + } + + if(!result) + { + // No existing session found. + + LL_DEBUGS("Voice") << "adding new session: handle \"" << handle << "\" URI " << uri << LL_ENDL; + result = sessionState::createSession(); + result->mSIPURI = uri; + result->mHandle = handle; + + if (LLVoiceClient::instance().getVoiceEffectEnabled()) + { + result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); + } + + 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 + // a mAuidoSession that does not match the session tracked in mSessionsByHandle + mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); + } + } + else + { + // Found an existing session + + if(uri != result->mSIPURI) + { + // TODO: Should this be an internal error? + LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; + setSessionURI(result, uri); + } + + if(handle != result->mHandle) + { + if(handle.empty()) + { + // There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. + LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; + } + else + { + // TODO: Should this be an internal error? + LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; + setSessionHandle(result, handle); + } + } + + LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; + } + + verifySessionState(); + + return result; +} + +void LLWebRTCVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) +{ + if (session) + { + if (!session->mHandle.empty()) + { + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if (iter != mSessionsByHandle.end()) + { + mSessionsByHandle.erase(iter); + } + } + else + { + LL_WARNS("Voice") << "Session has empty handle!" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "Attempt to clear NULL session!" << LL_ENDL; + } + +} + +void LLWebRTCVoiceClient::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. + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if(iter != mSessionsByHandle.end()) + { + if(iter->second != session) + { + LL_WARNS("Voice") << "Internal error: session mismatch! Session may have been duplicated. Removing version in map." << LL_ENDL; + } + + mSessionsByHandle.erase(iter); + } + else + { + LL_WARNS("Voice") << "Attempt to remove session with handle " << session->mHandle << " not found in map!" << LL_ENDL; + } + } + + session->mHandle = handle; + + if(!handle.empty()) + { + mSessionsByHandle.insert(sessionMap::value_type(session->mHandle, session)); + } + + verifySessionState(); +} + +void LLWebRTCVoiceClient::setSessionURI(const sessionStatePtr_t &session, const std::string &uri) +{ + // There used to be a map of session URIs to sessions, which made this complex.... + session->mSIPURI = uri; + + verifySessionState(); +} + +void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) +{ + // Remove the session from the handle map + if(!session->mHandle.empty()) + { + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if(iter != mSessionsByHandle.end()) + { + if(iter->second != session) + { + LL_WARNS("Voice") << "Internal error: session mismatch, removing session in map." << LL_ENDL; + } + mSessionsByHandle.erase(iter); + } + } + + // At this point, the session should be unhooked from all lists and all state should be consistent. + verifySessionState(); + + // If this is the current audio session, clean up the pointer which will soon be dangling. + if(mAudioSession == session) + { + mAudioSession.reset(); + mAudioSessionChanged = true; + } + + // ditto for the next audio session + if(mNextAudioSession == session) + { + mNextAudioSession.reset(); + } + +} + +void LLWebRTCVoiceClient::deleteAllSessions() +{ + LL_DEBUGS("Voice") << LL_ENDL; + + while (!mSessionsByHandle.empty()) + { + const sessionStatePtr_t session = mSessionsByHandle.begin()->second; + deleteSession(session); + } + +} + +void LLWebRTCVoiceClient::verifySessionState(void) +{ + LL_DEBUGS("Voice") << "Sessions in handle map=" << mSessionsByHandle.size() << LL_ENDL; + sessionState::VerifySessions(); +} + +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) << " )" + << " mAudioSession=" << mAudioSession + << LL_ENDL; + + if(mAudioSession) + { + if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) + { + switch(mAudioSession->mErrorStatusCode) + { + case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; + case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; + case 20715: + //invalid channel, we may be using a set of poorly cached + //info + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + case 1009: + //invalid username and password + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + } + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + } + else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) + { + switch(mAudioSession->mErrorStatusCode) + { + case HTTP_NOT_FOUND: // NOT_FOUND + // *TODO: Should this be 503? + case 480: // TEMPORARILY_UNAVAILABLE + case HTTP_REQUEST_TIME_OUT: // REQUEST_TIMEOUT + // call failed because other user was not available + // treat this as an error case + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + break; + } + } + } + + LL_DEBUGS("Voice") + << " " << LLVoiceClientStatusObserver::status2string(status) + << ", session URI " << getAudioSessionURI() + << ", proximal is " << inSpatialChannel() + << LL_ENDL; + + for (status_observer_set_t::iterator it = mStatusObservers.begin(); + it != mStatusObservers.end(); + ) + { + LLVoiceClientStatusObserver* observer = *it; + observer->onChange(status, getAudioSessionURI(), 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) +{ + mFriendObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) +{ + mFriendObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyFriendObservers() +{ + for (friend_observer_set_t::iterator it = mFriendObservers.begin(); + it != mFriendObservers.end(); + ) + { + LLFriendObserver* observer = *it; + it++; + // The only friend-related thing we notify on is online/offline transitions. + observer->changed(LLFriendObserver::ONLINE); + } +} + +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->mAccountName = name; + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; + } + + // Check whether this is a p2p session whose caller name just resolved + if (session->mCallerID == id) + { + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; + if (session->mTextInvitePending) + { + session->mTextInvitePending = false; + + // 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, + session->mCallerID, + session->mName, + IM_SESSION_P2P_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE, + session->mHandle, + session->mSIPURI); + } + + } +} + +void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +{ + sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); +} + +bool LLWebRTCVoiceClient::setVoiceEffect(const LLUUID& id) +{ + if (!mAudioSession) + { + return false; + } + + if (!id.isNull()) + { + if (mVoiceFontMap.empty()) + { + LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; + return false; + } + else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) + { + LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; + return false; + } + } + + // *TODO: Check for expired fonts? + mAudioSession->mVoiceFontID = id; + + // *TODO: Separate voice font defaults for spatial chat and IM? + gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); + + sessionSetVoiceFontSendMessage(mAudioSession); + notifyVoiceFontObservers(); + + return true; +} + +const LLUUID LLWebRTCVoiceClient::getVoiceEffect() +{ + return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; +} + +LLSD LLWebRTCVoiceClient::getVoiceEffectProperties(const LLUUID& id) +{ + LLSD sd; + + voice_font_map_t::iterator iter = mVoiceFontMap.find(id); + if (iter != mVoiceFontMap.end()) + { + sd["template_only"] = false; + } + else + { + // Voice effect is not in the voice font map, see if there is a template + iter = mVoiceFontTemplateMap.find(id); + if (iter == mVoiceFontTemplateMap.end()) + { + LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; + return sd; + } + sd["template_only"] = true; + } + + voiceFontEntry *font = iter->second; + sd["name"] = font->mName; + sd["expiry_date"] = font->mExpirationDate; + sd["is_new"] = font->mIsNew; + + return sd; +} + +LLWebRTCVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : + mID(id), + mFontIndex(0), + mFontType(VOICE_FONT_TYPE_NONE), + mFontStatus(VOICE_FONT_STATUS_NONE), + mIsNew(false) +{ + mExpiryTimer.stop(); + mExpiryWarningTimer.stop(); +} + +LLWebRTCVoiceClient::voiceFontEntry::~voiceFontEntry() +{ +} + +void LLWebRTCVoiceClient::refreshVoiceEffectLists(bool clear_lists) +{ + if (clear_lists) + { + mVoiceFontsReceived = false; + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); + } + + accountGetSessionFontsSendMessage(); + accountGetTemplateFontsSendMessage(); +} + +const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectList() const +{ + return mVoiceFontList; +} + +const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectTemplateList() const +{ + return mVoiceFontTemplateList; +} + +void LLWebRTCVoiceClient::addVoiceFont(const S32 font_index, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + const S32 font_status, + const bool template_font) +{ + // WebRTC SessionFontIDs are not guaranteed to remain the same between + // sessions or grids so use a UUID for the name. + + // If received name is not a UUID, fudge one by hashing the name and type. + LLUUID font_id; + if (LLUUID::validate(name)) + { + font_id = LLUUID(name); + } + else + { + font_id.generate(STRINGIZE(font_type << ":" << name)); + } + + voiceFontEntry *font = NULL; + + voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; + voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; + + // Check whether we've seen this font before. + voice_font_map_t::iterator iter = font_map.find(font_id); + bool new_font = (iter == font_map.end()); + + // Override the has_expired flag if we have passed the expiration_date as a double check. + if (expiration_date.secondsSinceEpoch() < (LLDate::now().secondsSinceEpoch() + VOICE_FONT_EXPIRY_INTERVAL)) + { + has_expired = true; + } + + if (has_expired) + { + LL_DEBUGS("VoiceFont") << "Expired " << (template_font ? "Template " : "") + << expiration_date.asString() << " " << font_id + << " (" << font_index << ") " << name << LL_ENDL; + + // Remove existing session fonts that have expired since we last saw them. + if (!new_font && !template_font) + { + deleteVoiceFont(font_id); + } + return; + } + + if (new_font) + { + // If it is a new font create a new entry. + font = new voiceFontEntry(font_id); + } + else + { + // Not a new font, update the existing entry + font = iter->second; + } + + if (font) + { + font->mFontIndex = font_index; + // Use the description for the human readable name if available, as the + // "name" may be a UUID. + font->mName = description.empty() ? name : description; + font->mFontType = font_type; + font->mFontStatus = font_status; + + // If the font is new or the expiration date has changed the expiry timers need updating. + if (!template_font && (new_font || font->mExpirationDate != expiration_date)) + { + font->mExpirationDate = expiration_date; + + // Set the expiry timer to trigger a notification when the voice font can no longer be used. + font->mExpiryTimer.start(); + font->mExpiryTimer.setExpiryAt(expiration_date.secondsSinceEpoch() - VOICE_FONT_EXPIRY_INTERVAL); + + // Set the warning timer to some interval before actual expiry. + S32 warning_time = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); + if (warning_time != 0) + { + font->mExpiryWarningTimer.start(); + F64 expiry_time = (expiration_date.secondsSinceEpoch() - (F64)warning_time); + font->mExpiryWarningTimer.setExpiryAt(expiry_time - VOICE_FONT_EXPIRY_INTERVAL); + } + else + { + // Disable the warning timer. + font->mExpiryWarningTimer.stop(); + } + + // Only flag new session fonts after the first time we have fetched the list. + if (mVoiceFontsReceived) + { + font->mIsNew = true; + mVoiceFontsNew = true; + } + } + + LL_DEBUGS("VoiceFont") << (template_font ? "Template " : "") + << font->mExpirationDate.asString() << " " << font->mID + << " (" << font->mFontIndex << ") " << name << LL_ENDL; + + if (new_font) + { + font_map.insert(voice_font_map_t::value_type(font->mID, font)); + font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); + } + + mVoiceFontListDirty = true; + + // Debugging stuff + + if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) + { + LL_WARNS("VoiceFont") << "Unknown voice font type: " << font_type << LL_ENDL; + } + if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) + { + LL_WARNS("VoiceFont") << "Unknown voice font status: " << font_status << LL_ENDL; + } + } +} + +void LLWebRTCVoiceClient::expireVoiceFonts() +{ + // *TODO: If we are selling voice fonts in packs, there are probably + // going to be a number of fonts with the same expiration time, so would + // be more efficient to just keep a list of expiration times rather + // than checking each font individually. + + bool have_expired = false; + bool will_expire = false; + bool expired_in_use = false; + + LLUUID current_effect = LLVoiceClient::instance().getVoiceEffectDefault(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + voiceFontEntry* voice_font = iter->second; + LLFrameTimer& expiry_timer = voice_font->mExpiryTimer; + LLFrameTimer& warning_timer = voice_font->mExpiryWarningTimer; + + // Check for expired voice fonts + if (expiry_timer.getStarted() && expiry_timer.hasExpired()) + { + // Check whether it is the active voice font + if (voice_font->mID == current_effect) + { + // Reset to no voice effect. + setVoiceEffect(LLUUID::null); + expired_in_use = true; + } + + LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " has expired." << LL_ENDL; + deleteVoiceFont(voice_font->mID); + have_expired = true; + } + + // Check for voice fonts that will expire in less that the warning time + if (warning_timer.getStarted() && warning_timer.hasExpired()) + { + LL_DEBUGS("VoiceFont") << "Voice Font " << voice_font->mName << " will expire soon." << LL_ENDL; + will_expire = true; + warning_timer.stop(); + } + } + + LLSD args; + args["URL"] = LLTrans::getString("voice_morphing_url"); + args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); + + // Give a notification if any voice fonts have expired. + if (have_expired) + { + if (expired_in_use) + { + LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); + } + else + { + LLNotificationsUtil::add("VoiceEffectsExpired", args); + } + + // Refresh voice font lists in the UI. + notifyVoiceFontObservers(); + } + + // Give a warning notification if any voice fonts are due to expire. + if (will_expire) + { + S32Seconds seconds(gSavedSettings.getS32("VoiceEffectExpiryWarningTime")); + args["INTERVAL"] = llformat("%d", LLUnit(seconds).value()); + + LLNotificationsUtil::add("VoiceEffectsWillExpire", args); + } +} + +void LLWebRTCVoiceClient::deleteVoiceFont(const LLUUID& id) +{ + // Remove the entry from the voice font list. + voice_effect_list_t::iterator list_iter = mVoiceFontList.begin(); + while (list_iter != mVoiceFontList.end()) + { + if (list_iter->second == id) + { + LL_DEBUGS("VoiceFont") << "Removing " << id << " from the voice font list." << LL_ENDL; + list_iter = mVoiceFontList.erase(list_iter); + mVoiceFontListDirty = true; + } + else + { + ++list_iter; + } + } + + // Find the entry in the voice font map and erase its data. + voice_font_map_t::iterator map_iter = mVoiceFontMap.find(id); + if (map_iter != mVoiceFontMap.end()) + { + delete map_iter->second; + } + + // Remove the entry from the voice font map. + mVoiceFontMap.erase(map_iter); +} + +void LLWebRTCVoiceClient::deleteAllVoiceFonts() +{ + mVoiceFontList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontMap.clear(); +} + +void LLWebRTCVoiceClient::deleteVoiceFontTemplates() +{ + mVoiceFontTemplateList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontTemplateMap.clear(); +} + +S32 LLWebRTCVoiceClient::getVoiceFontIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontMap.find(id); + if (it != mVoiceFontMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_WARNS("VoiceFont") << "Selected voice font " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +S32 LLWebRTCVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); + if (it != mVoiceFontTemplateMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_WARNS("VoiceFont") << "Selected voice font template " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +void LLWebRTCVoiceClient::accountGetSessionFontsSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("VoiceFont") << "Requesting voice font list." << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::accountGetTemplateFontsSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("VoiceFont") << "Requesting voice font template list." << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("VoiceFont") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "" + << "" << session->mHandle << "" + << "" << font_index << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) +{ + if (mIsWaitingForFonts) + { + // *TODO: We seem to get multiple events of this type. Should figure a way to advance only after + // receiving the last one. + LLSD result(LLSDMap("voice_fonts", LLSD::Boolean(true))); + + mWebRTCPump.post(result); + } + notifyVoiceFontObservers(); + mVoiceFontsReceived = true; +} + +void LLWebRTCVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + notifyVoiceFontObservers(); +} +void LLWebRTCVoiceClient::addObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.erase(observer); +} + +// method checks the item in VoiceMorphing menu for appropriate current voice font +bool LLWebRTCVoiceClient::onCheckVoiceEffect(const std::string& voice_effect_name) +{ + LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); + if (NULL != effect_interfacep) + { + const LLUUID& currect_voice_effect_id = effect_interfacep->getVoiceEffect(); + + if (currect_voice_effect_id.isNull()) + { + if (voice_effect_name == "NoVoiceMorphing") + { + return true; + } + } + else + { + const LLSD& voice_effect_props = effect_interfacep->getVoiceEffectProperties(currect_voice_effect_id); + if (voice_effect_props["name"].asString() == voice_effect_name) + { + return true; + } + } + } + + return false; +} + +// method changes voice font for selected VoiceMorphing menu item +void LLWebRTCVoiceClient::onClickVoiceEffect(const std::string& voice_effect_name) +{ + LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); + if (NULL != effect_interfacep) + { + if (voice_effect_name == "NoVoiceMorphing") + { + effect_interfacep->setVoiceEffect(LLUUID()); + return; + } + const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); + if (!effect_list.empty()) + { + for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + { + if (voice_effect_name == it->first) + { + effect_interfacep->setVoiceEffect(it->second); + return; + } + } + } + } +} + +// it updates VoiceMorphing menu items in accordance with purchased properties +void LLWebRTCVoiceClient::updateVoiceMorphingMenu() +{ + if (mVoiceFontListDirty) + { + LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interfacep) + { + const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); + if (!effect_list.empty()) + { + LLMenuGL * voice_morphing_menup = gMenuBarView->findChildMenuByName("VoiceMorphing", TRUE); + + if (NULL != voice_morphing_menup) + { + S32 items = voice_morphing_menup->getItemCount(); + if (items > 0) + { + voice_morphing_menup->erase(1, items - 3, false); + + S32 pos = 1; + for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + { + LLMenuItemCheckGL::Params p; + p.name = it->first; + p.label = it->first; + p.on_check.function(boost::bind(&LLWebRTCVoiceClient::onCheckVoiceEffect, this, it->first)); + p.on_click.function(boost::bind(&LLWebRTCVoiceClient::onClickVoiceEffect, this, it->first)); + LLMenuItemCheckGL * voice_effect_itemp = LLUICtrlFactory::create(p); + voice_morphing_menup->insert(pos++, voice_effect_itemp, false); + } + + voice_morphing_menup->needsArrange(); + } + } + } + } + } +} +void LLWebRTCVoiceClient::notifyVoiceFontObservers() +{ + LL_DEBUGS("VoiceFont") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; + + updateVoiceMorphingMenu(); + + for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); + it != mVoiceFontObservers.end();) + { + LLVoiceEffectObserver* observer = *it; + observer->onVoiceEffectChanged(mVoiceFontListDirty); + // In case onVoiceEffectChanged() deleted an entry. + it = mVoiceFontObservers.upper_bound(observer); + } + mVoiceFontListDirty = false; + + // If new Voice Fonts have been added notify the user. + if (mVoiceFontsNew) + { + if (mVoiceFontsReceived) + { + LLNotificationsUtil::add("VoiceEffectsNew"); + } + mVoiceFontsNew = false; + } +} + +void LLWebRTCVoiceClient::enablePreviewBuffer(bool enable) +{ + LLSD result; + mCaptureBufferMode = enable; + + if (enable) + result["recplay"] = "start"; + else + result["recplay"] = "quit"; + + mWebRTCPump.post(result); + + if(mCaptureBufferMode && mIsInChannel) + { + LL_DEBUGS("Voice") << "no channel" << LL_ENDL; + sessionTerminate(); + } +} + +void LLWebRTCVoiceClient::recordPreviewBuffer() +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, cannot start recording." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + mCaptureBufferRecording = true; + + LLSD result(LLSDMap("recplay", "record")); + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::playPreviewBuffer(const LLUUID& effect_id) +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, no buffer to play." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + if (!mCaptureBufferRecorded) + { + // Can't play until we have something recorded! + mCaptureBufferPlaying = false; + return; + } + + mPreviewVoiceFont = effect_id; + mCaptureBufferPlaying = true; + + LLSD result(LLSDMap("recplay", "playback")); + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::stopPreviewBuffer() +{ + mCaptureBufferRecording = false; + mCaptureBufferPlaying = false; + + LLSD result(LLSDMap("recplay", "quit")); + mWebRTCPump.post(result); +} + +bool LLWebRTCVoiceClient::isPreviewRecording() +{ + return (mCaptureBufferMode && mCaptureBufferRecording); +} + +bool LLWebRTCVoiceClient::isPreviewPlaying() +{ + return (mCaptureBufferMode && mCaptureBufferPlaying); +} + +void LLWebRTCVoiceClient::captureBufferRecordStartSendMessage() +{ if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; + + // Start capture + stream + << "" + << "" + << "\n\n\n"; + + // Unmute the mic + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "false" + << "\n\n\n"; + + // Dirty the mute mic state so that it will get reset when we finishing previewing + mMuteMicDirty = true; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::captureBufferRecordStopSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; + + // Mute the mic. Mic mute state was dirtied at recording start, so will be reset when finished previewing. + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "true" + << "\n\n\n"; + + // Stop capture + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) +{ + if(mAccountLoggedIn) + { + // Track how may play requests are sent, so we know how many stop events to + // expect before play actually stops. + ++mPlayRequestCount; + + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; + + S32 font_index = getVoiceFontTemplateIndex(voice_font_id); + LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" << font_index << "" + << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +LLWebRTCProtocolParser::LLWebRTCProtocolParser() +{ + parser = XML_ParserCreate(NULL); + + reset(); +} + +void LLWebRTCProtocolParser::reset() +{ + responseDepth = 0; + ignoringTags = false; + accumulateText = false; + energy = 0.f; + hasText = false; + hasAudio = false; + hasVideo = false; + terminated = false; + ignoreDepth = 0; + isChannel = false; + incoming = false; + enabled = false; + isEvent = false; + isLocallyMuted = false; + isModeratorMuted = false; + isSpeaking = false; + participantType = 0; + returnCode = -1; + state = 0; + statusCode = 0; + volume = 0; + textBuffer.clear(); + alias.clear(); + numberOfAliases = 0; + applicationString.clear(); +} + +//virtual +LLWebRTCProtocolParser::~LLWebRTCProtocolParser() +{ + if (parser) + XML_ParserFree(parser); +} + +static LLTrace::BlockTimerStatHandle FTM_WebRTC_PROCESS("WebRTC Process"); + +// virtual +LLIOPipe::EStatus LLWebRTCProtocolParser::process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + LLSD& context, + LLPumpIO* pump) +{ + LL_RECORD_BLOCK_TIME(FTM_WebRTC_PROCESS); + LLBufferStream istr(channels, buffer.get()); + std::ostringstream ostr; + while (istr.good()) + { + char buf[1024]; + istr.read(buf, sizeof(buf)); + mInput.append(buf, istr.gcount()); + } + + // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. + int start = 0; + int delim; + while((delim = mInput.find("\n\n\n", start)) != std::string::npos) + { + + // Reset internal state of the LLWebRTCProtocolParser (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_Parse(parser, mInput.data() + start, delim - start, false); + + LL_DEBUGS("WebRTCProtocolParser") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; + start = delim + 3; + } + + if(start != 0) + mInput = mInput.substr(start); + + LL_DEBUGS("WebRTCProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; + + if(!LLWebRTCVoiceClient::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; +} + +void XMLCALL LLWebRTCProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) +{ + if (data) + { + LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; + object->StartTag(el, attr); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLWebRTCProtocolParser::ExpatEndTag(void *data, const char *el) +{ + if (data) + { + LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; + object->EndTag(el); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLWebRTCProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) +{ + if (data) + { + LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; + object->CharData(s, len); + } +} + +// -------------------------------------------------------------------------------- + + +void LLWebRTCProtocolParser::StartTag(const char *tag, const char **attr) +{ + // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags + 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 + while (*attr) + { + const char *key = *attr++; + const char *value = *attr++; + + if (!stricmp("requestId", key)) + { + requestId = value; + } + else if (!stricmp("action", key)) + { + actionString = value; + } + else if (!stricmp("type", key)) + { + eventTypeString = value; + } + } + } + LL_DEBUGS("WebRTCProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; + } + else + { + if (ignoringTags) + { + LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + else + { + LL_DEBUGS("WebRTCProtocolParser") << 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("WebRTCProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; + } + else if (!stricmp("CaptureDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->clearCaptureDevices(); + } + else if (!stricmp("RenderDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->clearRenderDevices(); + } + else if (!stricmp("CaptureDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("RenderDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("SessionFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("TemplateFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType.clear(); + } + } + } + responseDepth++; +} + +// -------------------------------------------------------------------------------- + +void LLWebRTCProtocolParser::EndTag(const char *tag) +{ + const std::string& string = textBuffer; + + responseDepth--; + + if (ignoringTags) + { + if (ignoreDepth == responseDepth) + { + LL_DEBUGS("WebRTCProtocolParser") << "end of ignore" << LL_ENDL; + ignoringTags = false; + } + else + { + LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + } + + if (!ignoringTags) + { + LL_DEBUGS("WebRTCProtocolParser") << "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); + else if (!stricmp("SessionHandle", tag)) + sessionHandle = string; + else if (!stricmp("SessionGroupHandle", tag)) + sessionGroupHandle = string; + else if (!stricmp("StatusCode", tag)) + statusCode = strtol(string.c_str(), NULL, 10); + else if (!stricmp("StatusString", tag)) + statusString = string; + else if (!stricmp("ParticipantURI", tag)) + uriString = string; + else if (!stricmp("Volume", tag)) + volume = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Energy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("IsModeratorMuted", tag)) + isModeratorMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("IsSpeaking", tag)) + isSpeaking = !stricmp(string.c_str(), "true"); + else if (!stricmp("Alias", tag)) + alias = string; + else if (!stricmp("NumberOfAliases", tag)) + numberOfAliases = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Application", tag)) + applicationString = string; + else if (!stricmp("ConnectorHandle", tag)) + connectorHandle = string; + else if (!stricmp("VersionID", tag)) + versionID = string; + else if (!stricmp("Version", tag)) + mBuildID = string; + else if (!stricmp("AccountHandle", tag)) + accountHandle = string; + else if (!stricmp("State", tag)) + state = strtol(string.c_str(), NULL, 10); + else if (!stricmp("URI", tag)) + uriString = string; + else if (!stricmp("IsChannel", tag)) + isChannel = !stricmp(string.c_str(), "true"); + else if (!stricmp("Incoming", tag)) + incoming = !stricmp(string.c_str(), "true"); + else if (!stricmp("Enabled", tag)) + enabled = !stricmp(string.c_str(), "true"); + else if (!stricmp("Name", tag)) + nameString = string; + else if (!stricmp("AudioMedia", tag)) + audioMediaString = string; + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("DisplayName", tag)) + displayNameString = string; + else if (!stricmp("Device", tag)) + deviceString = string; + else if (!stricmp("AccountName", tag)) + nameString = string; + else if (!stricmp("ParticipantType", tag)) + participantType = strtol(string.c_str(), NULL, 10); + else if (!stricmp("IsLocallyMuted", tag)) + isLocallyMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("MicEnergy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("ChannelURI", tag)) + uriString = string; + else if (!stricmp("BuddyURI", tag)) + uriString = string; + else if (!stricmp("Presence", tag)) + statusString = string; + else if (!stricmp("CaptureDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); + } + else if (!stricmp("RenderDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); + } + else if (!stricmp("CaptureDevice", tag)) + { + LLWebRTCVoiceClient::getInstance()->addCaptureDevice(LLVoiceDevice(displayNameString, deviceString)); + } + else if (!stricmp("RenderDevice", tag)) + { + LLWebRTCVoiceClient::getInstance()->addRenderDevice(LLVoiceDevice(displayNameString, deviceString)); + } + else if (!stricmp("BlockMask", tag)) + blockMask = string; + else if (!stricmp("PresenceOnly", tag)) + presenceOnly = string; + else if (!stricmp("AutoAcceptMask", tag)) + autoAcceptMask = string; + else if (!stricmp("AutoAddAsBuddy", tag)) + autoAddAsBuddy = string; + else if (!stricmp("MessageHeader", tag)) + messageHeader = string; + else if (!stricmp("MessageBody", tag)) + messageBody = string; + else if (!stricmp("NotificationType", tag)) + notificationType = string; + else if (!stricmp("HasText", tag)) + hasText = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasAudio", tag)) + hasAudio = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasVideo", tag)) + hasVideo = !stricmp(string.c_str(), "true"); + else if (!stricmp("Terminated", tag)) + terminated = !stricmp(string.c_str(), "true"); + else if (!stricmp("SubscriptionHandle", tag)) + subscriptionHandle = string; + else if (!stricmp("SubscriptionType", tag)) + subscriptionType = string; + else if (!stricmp("SessionFont", tag)) + { + LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, false); + } + else if (!stricmp("TemplateFont", tag)) + { + LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, true); + } + else if (!stricmp("ID", tag)) + { + id = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Description", tag)) + { + descriptionString = string; + } + else if (!stricmp("ExpirationDate", tag)) + { + expirationDate = expiryTimeStampToLLDate(string); + } + else if (!stricmp("Expired", tag)) + { + hasExpired = !stricmp(string.c_str(), "1"); + } + else if (!stricmp("Type", tag)) + { + fontType = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Status", tag)) + { + fontStatus = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType = string;; + } + + textBuffer.clear(); + accumulateText= false; + + if (responseDepth == 0) + { + // We finished all of the XML, process the data + processResponse(tag); + } + } +} + +// -------------------------------------------------------------------------------- + +void LLWebRTCProtocolParser::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) + textBuffer.append(buffer, length); +} + +// -------------------------------------------------------------------------------- + +LLDate LLWebRTCProtocolParser::expiryTimeStampToLLDate(const std::string& WebRTC_ts) +{ + // *HACK: WebRTC reports the time incorrectly. LLDate also only parses a + // subset of valid ISO 8601 dates (only handles Z, not offsets). + // So just use the date portion and fix the time here. + std::string time_stamp = WebRTC_ts.substr(0, 10); + time_stamp += VOICE_FONT_EXPIRY_TIME; + + LL_DEBUGS("WebRTCProtocolParser") << "WebRTC timestamp " << WebRTC_ts << " modified to: " << time_stamp << LL_ENDL; + + return LLDate(time_stamp); +} + +// -------------------------------------------------------------------------------- + +void LLWebRTCProtocolParser::processResponse(std::string tag) +{ + LL_DEBUGS("WebRTCProtocolParser") << 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(); + LL_DEBUGS("LowVoice") << eventTypeCstr << LL_ENDL; + + if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) + { + // These happen so often that logging them is pretty useless. + LL_DEBUGS("LowVoice") << "Updated Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << isModeratorMuted << ", " << isSpeaking << ", " << volume << ", " << energy << LL_ENDL; + LLWebRTCVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); + } + else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) + { + LLWebRTCVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); + } + else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-1408789@bhr.WebRTC.com + true + false + + + */ + LLWebRTCVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) + { + LLWebRTCVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent")) + { + //nothng useful to process for this event, but we should not WARN that we have received it. + } + else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) + { + LLWebRTCVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + 200 + OK + 2 + false + + */ + LLWebRTCVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); + } + else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) + { + /* + + + AuxBufferAudioCapture + + */ + LLWebRTCVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); + } + else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.WebRTC.com + xI5auBZ60SJWIk606-1JGRQ== + + 0 + + */ + LL_DEBUGS("LowVoice") << "Added Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << ", " << displayNameString << ", " << participantType << LL_ENDL; + LLWebRTCVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); + } + else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.WebRTC.com + xtx7YNV-3SGiG7rA1fo5Ndw== + + */ + LL_DEBUGS("LowVoice") << "Removed params:" << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << LL_ENDL; + + LLWebRTCVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); + } + else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) + { + // These are really spammy in tuning mode + LLWebRTCVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); + } + else if (!stricmp(eventTypeCstr, "MessageEvent")) + { + //TODO: This probably is not received any more, it was used to support SLim clients + LLWebRTCVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) + { + //TODO: This probably is not received any more, it was used to support SLim clients + LLWebRTCVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); + } + else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-9@bhd.WebRTC.com + 0 + 50 + 1 + 0 + 000 + 0 + + */ + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) + { + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) + { + LLWebRTCVoiceClient::getInstance()->voiceServiceConnectionStateChangedEvent(statusCode, statusString, mBuildID); + } + else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent")) + { + /* + + RenderDeviceChanged< / EventType> + + Speakers(Turtle Beach P11 Headset)< / Device> + Speakers(Turtle Beach P11 Headset)< / DisplayName> + SpecificDevice< / Type> + < / RelevantDevice> + < / Event> + */ + // an audio device was removed or added, fetch and update the local list of audio devices. + } + else + { + LL_WARNS("WebRTCProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; + } + } + else + { + const char *actionCstr = actionString.c_str(); + LL_DEBUGS("LowVoice") << actionCstr << LL_ENDL; + + if (!stricmp(actionCstr, "Session.Set3DPosition.1")) + { + // We don't need to process these + } + else if (!stricmp(actionCstr, "Connector.Create.1")) + { + LLWebRTCVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); + } + else if (!stricmp(actionCstr, "Account.Login.1")) + { + LLWebRTCVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); + } + else if (!stricmp(actionCstr, "Session.Create.1")) + { + LLWebRTCVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) + { + LLWebRTCVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "Session.Connect.1")) + { + LLWebRTCVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.Logout.1")) + { + LLWebRTCVoiceClient::getInstance()->logoutResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) + { + LLWebRTCVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) + { + LLWebRTCVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) + { + LLWebRTCVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Aux.SetVadProperties.1")) + { + // both values of statusCode (old and more recent) indicate valid requests + if (statusCode != 0 && statusCode != 200) + { + LL_WARNS("Voice") << "Aux.SetVadProperties.1 request failed: " + << "statusCode: " << statusCode + << " and " + << "statusString: " << statusString + << LL_ENDL; + } + } + /* + else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) + { + LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString); + } + 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")) + { + + } + */ + } +} + +LLWebRTCSecurity::LLWebRTCSecurity() +{ + // This size is an arbitrary choice; WebRTC does not care + // Use a multiple of three so that there is no '=' padding in the base64 (purely an esthetic choice) + #define WebRTC_TOKEN_BYTES 9 + U8 random_value[WebRTC_TOKEN_BYTES]; + + for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) + { + random_value[b] = ll_rand() & 0xff; + } + mConnectorHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); + + for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) + { + random_value[b] = ll_rand() & 0xff; + } + mAccountHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); +} + +LLWebRTCSecurity::~LLWebRTCSecurity() +{ +} diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h new file mode 100644 index 0000000000..56ca74f6e1 --- /dev/null +++ b/indra/newview/llvoicewebrtc.h @@ -0,0 +1,1102 @@ +/** + * @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 LLVOAvatar; +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 + +#ifdef LL_USESYSTEMLIBS +# include "expat.h" +#else +# include "expat/expat.h" +#endif +#include "llvoiceclient.h" + +// WebRTC Includes +#include + +class LLAvatarName; +class LLWebRTCVoiceClientMuteListObserver; + + +class LLWebRTCVoiceClient : public LLSingleton, + virtual public LLVoiceModuleInterface, + virtual public LLVoiceEffectInterface, + public llwebrtc::LLWebRTCDevicesObserver, + public llwebrtc::LLWebRTCSignalingObserver +{ + LLSINGLETON(LLWebRTCVoiceClient); + LOG_CLASS(LLWebRTCVoiceClient); + virtual ~LLWebRTCVoiceClient(); + +public: + /// @name LLVoiceModuleInterface virtual implementations + /// @see LLVoiceModuleInterface + //@{ + virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) + virtual void terminate(); // Call this to clean up during shutdown + + virtual const LLVoiceVersionInfo& getVersion(); + + virtual void updateSettings(); // call after loading settings and whenever they change + + // Returns true if WebRTC has successfully logged in and is not in error state + virtual bool isVoiceWorking() const; + + ///////////////////// + /// @name Tuning + //@{ + virtual void tuningStart(); + virtual void tuningStop(); + virtual bool inTuningMode(); + + virtual void tuningSetMicVolume(float volume); + virtual void tuningSetSpeakerVolume(float volume); + virtual float tuningGetEnergy(void); + //@} + + ///////////////////// + /// @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(); + virtual bool deviceSettingsUpdated(); //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. + virtual void refreshDeviceLists(bool clearCurrentList = true); + + virtual void setCaptureDevice(const std::string& name); + virtual void setRenderDevice(const std::string& name); + + virtual LLVoiceDeviceList& getCaptureDevices(); + virtual LLVoiceDeviceList& getRenderDevices(); + //@} + + virtual void getParticipantList(std::set &participants); + virtual bool isParticipant(const LLUUID& speaker_id); + + // 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); + + // 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); + + // 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); + + + //////////////////////////// + /// @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(); + + virtual void setNonSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual bool setSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual void leaveNonSpatialChannel(); + + virtual void leaveChannel(void); + + // 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(); + //@} + + + ////////////////////////// + /// @name invitations + //@{ + // start a voice channel with the specified user + virtual void callUser(const LLUUID &uuid); + virtual bool isValidChannel(std::string &channelHandle); + virtual bool answerInvite(std::string &channelHandle); + virtual void declineInvite(std::string &channelHandle); + //@} + + ///////////////////////// + /// @name Volume/gain + //@{ + virtual void setVoiceVolume(F32 volume); + virtual void setMicGain(F32 volume); + //@} + + ///////////////////////// + /// @name enable disable voice and features + //@{ + virtual bool voiceEnabled(); + virtual void setVoiceEnabled(bool enabled); + virtual BOOL lipSyncEnabled(); + virtual void setLipSyncEnabled(BOOL enabled); + virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + //@} + + ////////////////////////// + /// @name nearby speaker accessors + //@{ + virtual BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar + virtual std::string getDisplayName(const LLUUID& id); + virtual BOOL isParticipantAvatar(const LLUUID &id); + virtual BOOL getIsSpeaking(const LLUUID& id); + virtual BOOL getIsModeratorMuted(const LLUUID& id); + virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + virtual BOOL getOnMuteList(const LLUUID& id); + virtual F32 getUserVolume(const LLUUID& id); + virtual void setUserVolume(const LLUUID& id, F32 volume); // 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); + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceClientStatusObserver* observer); + virtual void removeObserver(LLVoiceClientStatusObserver* observer); + virtual void addObserver(LLFriendObserver* observer); + virtual void removeObserver(LLFriendObserver* observer); + virtual void addObserver(LLVoiceClientParticipantObserver* observer); + virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + //@} + + virtual std::string sipURIFromID(const LLUUID &id); + //@} + + /// @name LLVoiceEffectInterface virtual implementations + /// @see LLVoiceEffectInterface + //@{ + + ////////////////////////// + /// @name Accessors + //@{ + virtual bool setVoiceEffect(const LLUUID& id); + virtual const LLUUID getVoiceEffect(); + virtual LLSD getVoiceEffectProperties(const LLUUID& id); + + virtual void refreshVoiceEffectLists(bool clear_lists); + virtual const voice_effect_list_t& getVoiceEffectList() const; + virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceEffectObserver* observer); + virtual void removeObserver(LLVoiceEffectObserver* observer); + //@} + + ////////////////////////////// + /// @name Devices change notification + // LLWebRTCDevicesObserver + //@{ + void OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; + void OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; + //@} + + ////////////////////////////// + /// @name Signaling notification + // LLWebRTCSignalingObserver + //@{ + void OnIceGatheringState(IceGatheringState state) override; + void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; + void OnOfferAvailable(const std::string &sdp) override; + void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + //@} + + void processIceUpdates(); + void onIceUpdateComplete(const LLSD& result); + void onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result); + + + ////////////////////////////// + /// @name Effect preview buffer + //@{ + virtual void enablePreviewBuffer(bool enable); + virtual void recordPreviewBuffer(); + virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); + virtual void stopPreviewBuffer(); + + virtual bool isPreviewRecording(); + virtual bool isPreviewPlaying(); + //@} + + //@} + + bool onCheckVoiceEffect(const std::string& voice_effect_name); + void onClickVoiceEffect(const std::string& voice_effect_name); + +protected: + ////////////////////// + // WebRTC Specific definitions + + friend class LLWebRTCVoiceClientMuteListObserver; + friend class LLWebRTCVoiceClientFriendsObserver; + + + enum streamState + { + streamStateUnknown = 0, + streamStateIdle = 1, + streamStateConnected = 2, + streamStateRinging = 3, + streamStateConnecting = 6, // same as WebRTC session_media_connecting enum + streamStateDisconnecting = 7, //Same as WebRTC 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; + std::string mDisplayName; + LLFrameTimer mSpeakingTimeout; + F32 mLastSpokeTimestamp; + F32 mPower; + F32 mVolume; + std::string mGroupID; + int mUserVolume; + bool mPTT; + bool mIsSpeaking; + bool mIsModeratorMuted; + bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) + bool mVolumeSet; // true if incoming volume messages should not change the volume + bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) + bool mAvatarIDValid; + bool mIsSelf; + }; + typedef boost::shared_ptr participantStatePtr_t; + typedef boost::weak_ptr participantStateWptr_t; + + typedef std::map participantMap; + typedef std::map participantUUIDMap; + + struct sessionState + { + public: + typedef boost::shared_ptr ptr_t; + typedef boost::weak_ptr wptr_t; + + typedef boost::function sessionFunc_t; + + static ptr_t createSession(); + ~sessionState(); + + participantStatePtr_t addParticipant(const std::string &uri); + void removeParticipant(const participantStatePtr_t &participant); + void removeAllParticipants(); + + participantStatePtr_t findParticipant(const std::string &uri); + participantStatePtr_t findParticipantByID(const LLUUID& id); + + static ptr_t matchSessionByHandle(const std::string &handle); + static ptr_t matchCreatingSessionByURI(const std::string &uri); + static ptr_t matchSessionByURI(const std::string &uri); + static ptr_t matchSessionByParticipant(const LLUUID &participant_id); + + bool isCallBackPossible(); + bool isTextIMPossible(); + + static void for_each(sessionFunc_t func); + + std::string mHandle; + std::string mGroupHandle; + std::string mSIPURI; + std::string mAlias; + std::string mName; + std::string mAlternateSIPURI; + std::string mHash; // Channel password + std::string mErrorStatusString; + std::queue mTextMsgQueue; + + LLUUID mIMSessionID; + LLUUID mCallerID; + int mErrorStatusCode; + int mMediaStreamState; + bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. + bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. + bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) + bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) + bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. + bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) + bool mIsSpatial; // True for spatial channels + bool mIsP2P; + bool mIncoming; + bool mVoiceActive; + bool mReconnect; // Whether we should try to reconnect to this session if it's dropped + + // Set to true when the volume/mute state of someone in the participant list changes. + // The code will have to walk the list to find the changed participant(s). + bool mVolumeDirty; + bool mMuteDirty; + + bool mParticipantsChanged; + participantMap mParticipantsByURI; + participantUUIDMap mParticipantsByUUID; + + LLUUID mVoiceFontID; + + static void VerifySessions(); + + private: + sessionState(); + + static std::set mSession; // canonical list of outstanding sessions. + std::set::iterator mMyIterator; // used for delete + + static void for_eachPredicate(const wptr_t &a, sessionFunc_t func); + + static bool testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle); + static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); + + }; + typedef boost::shared_ptr sessionStatePtr_t; + + typedef std::map sessionMap; + + /////////////////////////////////////////////////////// + // Private Member Functions + ////////////////////////////////////////////////////// + + + + ////////////////////////////// + /// @name TVC/Server management and communication + //@{ + // 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(); + + // write to the tvc + bool writeString(const std::string &str); + + void connectorCreate(); + void connectorShutdown(); + void closeSocket(void); + +// void requestVoiceAccountProvision(S32 retries = 3); + void setLoginInfo( + const std::string& account_name, + const std::string& password, + const std::string& channel_sdp); + void loginSendMessage(); + void logout(); + void logoutSendMessage(); + + + //@} + + //------------------------------------ + // tuning + + void tuningRenderStartSendMessage(const std::string& name, bool loop); + void tuningRenderStopSendMessage(); + + void tuningCaptureStartSendMessage(int duration); + void tuningCaptureStopSendMessage(); + + //---------------------------------- + // devices + void clearCaptureDevices(); + void addCaptureDevice(const LLVoiceDevice& device); + + void clearRenderDevices(); + void addRenderDevice(const LLVoiceDevice& device); + void setDevicesListUpdated(bool state); + void buildSetAudioDevices(std::ostringstream &stream); + + // local audio updates, mic mute, speaker mute, mic volume and speaker volumes + void sendLocalAudioUpdates(); + + ///////////////////////////// + // Response/Event handlers + void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); + void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); + void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); + void logoutResponse(int statusCode, std::string &statusString); + void connectorShutdownResponse(int statusCode, std::string &statusString); + + void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); + void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType); + void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); + void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); + void sessionGroupAddedEvent(std::string &sessionGroupHandle); + void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); + void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); + void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); + void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); + void voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id); + 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 ¬ificationType); + + void muteListChanged(); + + ///////////////////////////// + // VAD changes + // disable auto-VAD and configure VAD parameters explicitly + void setupVADParams(unsigned int vad_auto, unsigned int vad_hangover, unsigned int vad_noise_floor, unsigned int vad_sensitivity); + void onVADSettingsChange(); + + ///////////////////////////// + // Sending updates of current state + void updatePosition(void); + void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); + bool channelFromRegion(LLViewerRegion *region, std::string &name); + + void setEarLocation(S32 loc); + + + ///////////////////////////// + // Accessors for data related to nearby speakers + + // MBW -- XXX -- Not sure how to get this data out of the TVC + BOOL getUsingPTT(const LLUUID& id); + 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 sessionSet; + + typedef sessionSet::iterator sessionIterator; + sessionIterator sessionsBegin(void); + sessionIterator sessionsEnd(void); +#endif + + 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); + void setSessionURI(const sessionStatePtr_t &session, const std::string &uri); + void deleteSession(const sessionStatePtr_t &session); + void deleteAllSessions(void); + + void verifySessionState(void); + + void joinedAudioSession(const sessionStatePtr_t &session); + void leftAudioSession(const sessionStatePtr_t &session); + + // 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 + { + buddyListEntry(const std::string &uri); + std::string mURI; + std::string mDisplayName; + LLUUID mUUID; + bool mOnlineSL; + bool mOnlineSLim; + bool mCanSeeMeOnline; + bool mHasBlockListEntry; + bool mHasAutoAcceptListEntry; + bool mNameResolved; + bool mInSLFriends; + bool mInWebRTCBuddies; + }; + + typedef std::map 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); + void sessionMediaConnectSendMessage(const sessionStatePtr_t &session); // just joins the audio session + void sessionTextConnectSendMessage(const sessionStatePtr_t &session); // just joins the text session + void sessionTerminateSendMessage(const sessionStatePtr_t &session); + void sessionGroupTerminateSendMessage(const sessionStatePtr_t &session); + void sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session); + // void sessionTextDisconnectSendMessage(sessionState *session); + + + + // Pokes the state machine to leave the audio session next time around. + 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 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; + + ///////////////////////////// + // Voice fonts + + void addVoiceFont(const S32 id, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + 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); + +private: + + LLVoiceVersionInfo mVoiceVersion; + + // Coroutine support methods + //--- + void voiceControlCoro(); + void voiceControlStateMachine(); + int mVoiceControlState; + + bool endAndDisconnectSession(); + + bool callbackEndDaemon(const LLSD& data); + bool provisionVoiceAccount(); + void OnVoiceAccountProvisioned(const LLSD& body); + void OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result); + bool establishVoiceConnection(); + bool breakVoiceConnection(bool wait); + bool loginToWebRTC(); + void logoutOfWebRTC(bool wait); + + bool requestParcelVoiceInfo(); + + bool addAndJoinSession(const sessionStatePtr_t &nextSession); + bool terminateAudioSession(bool wait); + + bool waitForChannel(); + bool runSession(const sessionStatePtr_t &session); + + void recordingAndPlaybackMode(); + int voiceRecordBuffer(); + int voicePlaybackBuffer(); + + bool performMicTuning(); + //--- + /// Clean up objects created during a voice session. + void cleanUp(); + + bool mSessionTerminateRequested; + bool mRelogRequested; + // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). + // 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 + bool mTerminateDaemon; + + friend class LLWebRTCProtocolParser; + + std::string mAccountName; + std::string mAccountPassword; + std::string mChannelSDP; + std::string mRemoteChannelSDP; + std::string mAccountDisplayName; + + + bool mTuningMode; + float mTuningEnergy; + std::string mTuningAudioFile; + int mTuningMicVolume; + bool mTuningMicVolumeDirty; + int mTuningSpeakerVolume; + 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; + 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. + + sessionStatePtr_t mNextAudioSession; // Session state for the audio session we're trying to join + + 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 + int mNumberOfAliases; + U32 mCommandCookie; + + 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; + + llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; + llwebrtc::LLWebRTCSignalInterface *mWebRTCSignalingInterface; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + + LLVoiceDeviceList mCaptureDevices; + LLVoiceDeviceList mRenderDevices; + std::vector mIceCandidates; + bool mIceCompleted; + + 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); + + + bool inSpatialChannel(void); + std::string getAudioSessionURI(); + std::string getAudioSessionHandle(); + + void setHidden(bool hidden); //virtual + void sendPositionAndVolumeUpdate(void); + + void sendFriendsListUpdates(); + +#if 0 + // start a text IM session with the specified user + // This will be asynchronous, the session may be established at a future time. + sessionStatePtr_t startUserIMSession(const LLUUID& uuid); +#endif + + void enforceTether(void); + + bool mSpatialCoordsDirty; + + LLVector3d mCameraPosition; + LLVector3d mCameraRequestedPosition; + LLVector3 mCameraVelocity; + LLMatrix3 mCameraRot; + + 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; + + bool mSpeakerVolumeDirty; + bool mSpeakerMuteDirty; + int mSpeakerVolume; + + int mMicVolume; + bool mMicVolumeDirty; + + bool mVoiceEnabled; + bool mWriteInProgress; + std::string mWriteString; + size_t mWriteOffset; + + BOOL mLipSyncEnabled; + + typedef std::set observer_set_t; + observer_set_t mParticipantObservers; + + void notifyParticipantObservers(); + + typedef std::set status_observer_set_t; + status_observer_set_t mStatusObservers; + + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); + + typedef std::set friend_observer_set_t; + friend_observer_set_t mFriendObservers; + void notifyFriendObservers(); + + // Voice Fonts + + void expireVoiceFonts(); + void deleteVoiceFont(const LLUUID& id); + void deleteAllVoiceFonts(); + void deleteVoiceFontTemplates(); + + S32 getVoiceFontIndex(const LLUUID& id) const; + S32 getVoiceFontTemplateIndex(const LLUUID& id) const; + + void accountGetSessionFontsSendMessage(); + void accountGetTemplateFontsSendMessage(); + void sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session); + + void updateVoiceMorphingMenu(); + void notifyVoiceFontObservers(); + + typedef enum e_voice_font_type + { + VOICE_FONT_TYPE_NONE = 0, + VOICE_FONT_TYPE_ROOT = 1, + VOICE_FONT_TYPE_USER = 2, + VOICE_FONT_TYPE_UNKNOWN + } EVoiceFontType; + + typedef enum e_voice_font_status + { + VOICE_FONT_STATUS_NONE = 0, + VOICE_FONT_STATUS_FREE = 1, + VOICE_FONT_STATUS_NOT_FREE = 2, + VOICE_FONT_STATUS_UNKNOWN + } EVoiceFontStatus; + + struct voiceFontEntry + { + voiceFontEntry(LLUUID& id); + ~voiceFontEntry(); + + LLUUID mID; + S32 mFontIndex; + std::string mName; + LLDate mExpirationDate; + S32 mFontType; + S32 mFontStatus; + bool mIsNew; + + LLFrameTimer mExpiryTimer; + LLFrameTimer mExpiryWarningTimer; + }; + + bool mVoiceFontsReceived; + bool mVoiceFontsNew; + bool mVoiceFontListDirty; + voice_effect_list_t mVoiceFontList; + voice_effect_list_t mVoiceFontTemplateList; + + typedef std::map voice_font_map_t; + voice_font_map_t mVoiceFontMap; + voice_font_map_t mVoiceFontTemplateMap; + + typedef std::set voice_font_observer_set_t; + voice_font_observer_set_t mVoiceFontObservers; + + LLFrameTimer mVoiceFontExpiryTimer; + + + // Audio capture buffer + + void captureBufferRecordStartSendMessage(); + void captureBufferRecordStopSendMessage(); + void captureBufferPlayStartSendMessage(const LLUUID& voice_font_id = LLUUID::null); + void captureBufferPlayStopSendMessage(); + + bool mCaptureBufferMode; // Disconnected from voice channels while using the capture buffer. + bool mCaptureBufferRecording; // A voice sample is being captured. + bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play. + bool mCaptureBufferPlaying; // A voice sample is being played. + + LLTimer mCaptureTimer; + LLUUID mPreviewVoiceFont; + LLUUID mPreviewVoiceFontLast; + S32 mPlayRequestCount; + bool mIsInTuningMode; + bool mIsInChannel; + bool mIsJoiningSession; + bool mIsWaitingForFonts; + bool mIsLoggingIn; + bool mIsLoggedIn; + bool mIsProcessingChannels; + bool mIsCoroutineActive; + + // This variables can last longer than WebRTC in coroutines so we need them as static + static bool sShuttingDown; + static bool sConnected; + static LLPumpIO* sPump; + + LLEventMailDrop mWebRTCPump; +}; + + +/** + * @class LLWebRTCProtocolParser + * @brief This class helps construct new LLIOPipe specializations + * @see LLIOPipe + * + * THOROUGH_DESCRIPTION + */ +class LLWebRTCProtocolParser : public LLIOPipe +{ + LOG_CLASS(LLWebRTCProtocolParser); +public: + LLWebRTCProtocolParser(); + virtual ~LLWebRTCProtocolParser(); + +protected: + /* @name LLIOPipe virtual implementations + */ + //@{ + /** + * @brief Process the data in buffer + */ + virtual EStatus process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + 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; + std::string statusString; + std::string requestId; + std::string actionString; + std::string connectorHandle; + std::string versionID; + std::string mBuildID; + std::string accountHandle; + std::string sessionHandle; + 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; + std::string uriString; + bool isChannel; + bool incoming; + bool enabled; + std::string nameString; + std::string audioMediaString; + std::string deviceString; + std::string displayNameString; + int participantType; + bool isLocallyMuted; + bool isModeratorMuted; + bool isSpeaking; + int volume; + F32 energy; + std::string messageHeader; + std::string messageBody; + std::string notificationType; + bool hasText; + bool hasAudio; + bool hasVideo; + bool terminated; + std::string blockMask; + std::string presenceOnly; + std::string autoAcceptMask; + std::string autoAddAsBuddy; + int numberOfAliases; + std::string subscriptionHandle; + std::string subscriptionType; + S32 id; + std::string descriptionString; + LLDate expirationDate; + bool hasExpired; + 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); + LLDate expiryTimeStampToLLDate(const std::string& WebRTC_ts); + +}; + +class LLWebRTCSecurity : public LLSingleton +{ + LLSINGLETON(LLWebRTCSecurity); + virtual ~LLWebRTCSecurity(); + + public: + std::string connectorHandle() { return mConnectorHandle; }; + std::string accountHandle() { return mAccountHandle; }; + + private: + std::string mConnectorHandle; + std::string mAccountHandle; +}; + +class LLVoiceWebRTCStats : public LLSingleton +{ + 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(); +}; + +#endif //LL_WebRTC_VOICE_CLIENT_H + -- cgit v1.2.3 From 0e46396429c1cd9b188d0493be2fef93ad145920 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 8 Sep 2023 21:36:17 -0700 Subject: Pull webrtc down from a webserver before building. --- indra/cmake/WebRTC.cmake | 68 +++++++++++++++++++++---------------------- indra/llwebrtc/CMakeLists.txt | 6 ++-- 2 files changed, 37 insertions(+), 37 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index f8ce9c8104..7953d1ee1b 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -1,43 +1,41 @@ # -*- cmake -*- -include(CMakeCopyIfDifferent) -include(Linking) +include(FetchContent) -include_guard() - -set(WEBRTC_ROOT ${CMAKE_BINARY_DIR}/../../webrtc/src) -file(COPY ${WEBRTC_ROOT}/out/Default/obj/webrtc.lib - DESTINATION ${CMAKE_BINARY_DIR}/packages/lib/release +if (WINDOWS) +FetchContent_Declare( + webrtc + URL http://localhost:8000/webrtc.windows_x86_64.tar.bz2 + URL_HASH MD5=dfb692562770dc8c877ebfe4302e2881 + FIND_PACKAGE_ARGS NAMES webrtc + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" ) -set(WEBRTC_INCLUDE_DIR ${CMAKE_BINARY_DIR}/packages/include/webrtc) -file(MAKE_DIRECTORY ${WEBRTC_INCLUDE_DIR}) - -file(COPY ${WEBRTC_ROOT}/api - ${WEBRTC_ROOT}/media/base - ${WEBRTC_ROOT}/media/engine - ${WEBRTC_ROOT}/rtc_base - ${WEBRTC_ROOT}/pc - ${WEBRTC_ROOT}/p2p - ${WEBRTC_ROOT}/call - ${WEBRTC_ROOT}/media - ${WEBRTC_ROOT}/system_wrappers - ${WEBRTC_ROOT}/common_video - ${WEBRTC_ROOT}/video - ${WEBRTC_ROOT}/common_audio - ${WEBRTC_ROOT}/logging - ${WEBRTC_ROOT}/third_party/abseil-cpp/absl - DESTINATION ${WEBRTC_INCLUDE_DIR} - FILES_MATCHING PATTERN "*.h" +elseif (DARWIN) +FetchContent_Declare( + webrtc + URL http://localhost:8000/webrtc.macos_x86_64.tar.bz2 + URL_HASH MD5=cfbcac7da897a862f9791ea29330b814 + FIND_PACKAGE_ARGS NAMES webrtc + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" ) +endif (WINDOWS) + +FetchContent_MakeAvailable(webrtc) + +set(WEBRTC_PATH ${webrtc_SOURCE_DIR}) -add_library(ll::webrtc STATIC IMPORTED) -if (LINUX) - target_link_libraries( ll::webrtc INTERFACE ../webrtc/src/obj/Default/webrtc) +add_library( ll::webrtc INTERFACE IMPORTED ) + + +if (WINDOWS) + target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.lib" ) elseif (DARWIN) - target_link_libraries( ll::webrtc INTERFACE ../webrtc/src/obj/Default/webrtc) -elseif (WINDOWS) - set_target_properties( ll::webrtc PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/packages/lib/release/webrtc.lib) - target_link_libraries( ll::webrtc INTERFACE ${CMAKE_BINARY_DIR}/packages/lib/release/webrtc.lib) -endif (LINUX) -target_include_directories( ll::webrtc INTERFACE "${WEBRTC_INCLUDE_DIR}") + target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.a" ) +elseif (LINUX) + target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.a" ) +endif (WINDOWS) +target_include_directories( ll::webrtc SYSTEM INTERFACE "${WEBRTC_PATH}/include" "${WEBRTC_PATH}/include/third_party/abseil-cpp") + diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt index c0e2520a22..881a3af607 100644 --- a/indra/llwebrtc/CMakeLists.txt +++ b/indra/llwebrtc/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_GENERATOR_TOOLSET "clang_cl_x64") +#set(CMAKE_GENERATOR_TOOLSET "clang-cl") include(00-Common) include(Linking) @@ -20,6 +20,7 @@ message(STATUS "SharedLib: ${SHARED_LIB_STAGING_DIR}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") + set(llwebrtc_SOURCE_FILES llwebrtc.cpp ) @@ -46,7 +47,8 @@ target_link_libraries(llwebrtc PRIVATE ll::webrtc iphlpapi) target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -set_property(TARGET llwebrtc PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") +set_property(TARGET llwebrtc PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") install(TARGETS llwebrtc RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/sharedlibs/RelWithDebInfo") -- cgit v1.2.3 From db7ab5d73e748f86ee00e31965265969c0c7060f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 9 Sep 2023 22:19:22 -0700 Subject: Updates to build on mac. --- indra/cmake/00-Common.cmake | 2 + indra/cmake/WebRTC.cmake | 26 +++++-- indra/llwebrtc/CMakeLists.txt | 34 +++++---- indra/llwebrtc/llwebrtc_impl.h | 11 ++- indra/newview/llappviewer.h | 1 + indra/newview/llstartup.cpp | 2 +- indra/newview/llviewerregion.h | 1 + indra/newview/llvoicewebrtc.cpp | 21 ++---- indra/newview/llvoicewebrtc.h | 152 +++++++++++++++++++-------------------- indra/newview/viewer_manifest.py | 14 ++++ 10 files changed, 149 insertions(+), 115 deletions(-) (limited to 'indra') diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 24534c98d9..687ace431b 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -182,6 +182,8 @@ if (LINUX OR DARWIN) list(APPEND GCC_WARNINGS -Wno-reorder -Wno-non-virtual-dtor ) + list(APPEND GCC_WARNINGS -Wno-unused-but-set-variable -Wno-unused-variable ) + add_compile_options(${GCC_WARNINGS}) add_compile_options(-m${ADDRESS_SIZE}) endif (LINUX OR DARWIN) diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 7953d1ee1b..2040c86b9f 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -26,16 +26,30 @@ FetchContent_MakeAvailable(webrtc) set(WEBRTC_PATH ${webrtc_SOURCE_DIR}) - add_library( ll::webrtc INTERFACE IMPORTED ) - if (WINDOWS) - target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.lib" ) + target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.lib" ) elseif (DARWIN) - target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.a" ) + 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 + "${WEBRTC_PATH}/lib/libwebrtc.a" + ${COREAUDIO_LIBRARY} + ${AUDIOTOOLBOX_LIBRARY} + ${COREGRAPHICS_LIBRARY} + ${COREFOUNDATION_LIBRARY} + ${COCOA_LIBRARY} + ) elseif (LINUX) - target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.a" ) + target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/libwebrtc.a" ) endif (WINDOWS) -target_include_directories( ll::webrtc SYSTEM INTERFACE "${WEBRTC_PATH}/include" "${WEBRTC_PATH}/include/third_party/abseil-cpp") + +message("PATH: ${WEBRTC_PATH}/include") + +target_include_directories( ll::webrtc INTERFACE "${WEBRTC_PATH}/include" "${WEBRTC_PATH}/include/third_party/abseil-cpp") diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt index 881a3af607..fde4b87a3e 100644 --- a/indra/llwebrtc/CMakeLists.txt +++ b/indra/llwebrtc/CMakeLists.txt @@ -13,11 +13,6 @@ include(WebRTC) project(llwebrtc) -message(STATUS "C Compiler executable: ${CMAKE_C_COMPILER}") -message(STATUS "CXX Compiler executable: ${CMAKE_CXX_COMPILER}") -message(STATUS "Linker executable: ${CMAKE_LINKER}") -message(STATUS "SharedLib: ${SHARED_LIB_STAGING_DIR}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") @@ -34,10 +29,11 @@ set(llwebrtc_HEADER_FILES list(APPEND llwebrtc_SOURCE_FILES ${llwebrtc_HEADER_FILES}) add_library (llwebrtc SHARED ${llwebrtc_SOURCE_FILES}) - + set_target_properties(llwebrtc PROPERTIES PUBLIC_HEADER llwebrtc.h) -target_link_libraries(llwebrtc PRIVATE ll::webrtc +if (WINDOWS) + target_link_libraries(llwebrtc PRIVATE ll::webrtc secur32 winmm dmoguids @@ -45,13 +41,23 @@ target_link_libraries(llwebrtc PRIVATE ll::webrtc msdmo strmiids iphlpapi) -target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - -set_property(TARGET llwebrtc PROPERTY - MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") - -install(TARGETS llwebrtc RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/sharedlibs/RelWithDebInfo") - +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 + $ + ${SHARED_LIB_STAGING_DIR}) # Add tests if (LL_TESTS) endif (LL_TESTS) diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 10916e5a25..07c0f514b2 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -28,12 +28,21 @@ #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) #endif // WEBRTC_WIN + #include "api/scoped_refptr.h" #include "rtc_base/ref_count.h" #include "rtc_base/ref_counted_object.h" @@ -174,4 +183,4 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, } -#endif // LLWEBRTC_IMPL_H \ No newline at end of file +#endif // LLWEBRTC_IMPL_H 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/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 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/llviewerregion.h b/indra/newview/llviewerregion.h index a409d837a4..b24ff51479 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 diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 6e68ca7e4f..5fcfc969b5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -87,31 +87,19 @@ namespace { static const std::string VOICE_SERVER_TYPE = "WebRTC"; - // Don't retry connecting to the daemon more frequently than this: - const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; - const int DAEMON_CONNECT_RETRY_MAX = 3; - // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.5f; // Timeout for connection to WebRTC const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; const F32 CONNECT_DNS_TIMEOUT = 5.0f; - const int CONNECT_RETRY_MAX = 3; - const F32 LOGIN_ATTEMPT_TIMEOUT = 30.0f; const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; - const int LOGIN_RETRY_MAX = 3; - - const F32 PROVISION_RETRY_TIMEOUT = 2.0; - const int PROVISION_RETRY_MAX = 5; - + // 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); - 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 @@ -126,9 +114,6 @@ namespace { // Maximum length of capture buffer recordings in seconds. const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; - - const int ERROR_WebRTC_OBJECT_NOT_FOUND = 1001; - const int ERROR_WebRTC_NOT_LOGGED_IN = 1007; } static int scale_mic_volume(float volume) @@ -829,7 +814,7 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) { - channelSDP = result["jsep"]["sdp"]; + channelSDP = result["jsep"]["sdp"].asString(); } std::string voiceAccountServerUri; std::string voiceUserName = gAgent.getID().asString(); @@ -1468,6 +1453,8 @@ bool LLWebRTCVoiceClient::waitForChannel() << " VoiceEnabled=" << mVoiceEnabled << LL_ENDL; return !sShuttingDown; + case VOICE_CHANNEL_STATE_CHECK_EFFECTS: + break; } } while (true); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 56ca74f6e1..22c022ffdb 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -61,7 +61,7 @@ class LLWebRTCVoiceClient : public LLSingleton, public llwebrtc::LLWebRTCDevicesObserver, public llwebrtc::LLWebRTCSignalingObserver { - LLSINGLETON(LLWebRTCVoiceClient); + LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); virtual ~LLWebRTCVoiceClient(); @@ -69,26 +69,26 @@ public: /// @name LLVoiceModuleInterface virtual implementations /// @see LLVoiceModuleInterface //@{ - virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - virtual void terminate(); // Call this to clean up during shutdown + void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) + void terminate() override; // Call this to clean up during shutdown - virtual const LLVoiceVersionInfo& getVersion(); + const LLVoiceVersionInfo& getVersion() override; - virtual void updateSettings(); // call after loading settings and whenever they change + 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 - virtual bool isVoiceWorking() const; + bool isVoiceWorking() const override; ///////////////////// /// @name Tuning //@{ - virtual void tuningStart(); - virtual void tuningStop(); - virtual bool inTuningMode(); + void tuningStart() override; + void tuningStop() override; + bool inTuningMode() override; - virtual void tuningSetMicVolume(float volume); - virtual void tuningSetSpeakerVolume(float volume); - virtual float tuningGetEnergy(void); + void tuningSetMicVolume(float volume) override; + void tuningSetSpeakerVolume(float volume) override; + float tuningGetEnergy(void) override; //@} ///////////////////// @@ -96,40 +96,40 @@ public: //@{ // 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(); - virtual bool deviceSettingsUpdated(); //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 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. - virtual void refreshDeviceLists(bool clearCurrentList = true); + void refreshDeviceLists(bool clearCurrentList = true) override; - virtual void setCaptureDevice(const std::string& name); - virtual void setRenderDevice(const std::string& name); + void setCaptureDevice(const std::string& name) override; + void setRenderDevice(const std::string& name) override; - virtual LLVoiceDeviceList& getCaptureDevices(); - virtual LLVoiceDeviceList& getRenderDevices(); + LLVoiceDeviceList& getCaptureDevices() override; + LLVoiceDeviceList& getRenderDevices() override; //@} - virtual void getParticipantList(std::set &participants); - virtual bool isParticipant(const LLUUID& speaker_id); + void getParticipantList(std::set &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); + 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); + 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); + BOOL isSessionTextIMPossible(const LLUUID &session_id) override; //////////////////////////// @@ -137,21 +137,21 @@ public: //@{ // 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(); + bool inProximalChannel() override; - virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials); + void setNonSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual bool setSpatialChannel(const std::string &uri, - const std::string &credentials); + bool setSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual void leaveNonSpatialChannel(); + void leaveNonSpatialChannel() override; - virtual void leaveChannel(void); + 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(); + std::string getCurrentChannel() override; //@} @@ -159,59 +159,59 @@ public: /// @name invitations //@{ // start a voice channel with the specified user - virtual void callUser(const LLUUID &uuid); - virtual bool isValidChannel(std::string &channelHandle); - virtual bool answerInvite(std::string &channelHandle); - virtual void declineInvite(std::string &channelHandle); + void callUser(const LLUUID &uuid) override; + bool isValidChannel(std::string &channelHandle) override; + bool answerInvite(std::string &channelHandle) override; + void declineInvite(std::string &channelHandle) override; //@} ///////////////////////// /// @name Volume/gain //@{ - virtual void setVoiceVolume(F32 volume); - virtual void setMicGain(F32 volume); + void setVoiceVolume(F32 volume) override; + void setMicGain(F32 volume) override; //@} ///////////////////////// /// @name enable disable voice and features //@{ - virtual bool voiceEnabled(); - virtual void setVoiceEnabled(bool enabled); - virtual BOOL lipSyncEnabled(); - virtual void setLipSyncEnabled(BOOL enabled); - virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + bool voiceEnabled() override; + void setVoiceEnabled(bool enabled) override; + BOOL lipSyncEnabled() override; + void setLipSyncEnabled(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); // true if we've received data for this avatar - virtual std::string getDisplayName(const LLUUID& id); - virtual BOOL isParticipantAvatar(const LLUUID &id); - virtual BOOL getIsSpeaking(const LLUUID& id); - virtual BOOL getIsModeratorMuted(const LLUUID& id); - virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - virtual BOOL getOnMuteList(const LLUUID& id); - virtual F32 getUserVolume(const LLUUID& id); - virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar + 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... + BOOL getOnMuteList(const LLUUID& id) override; + 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); + void userAuthorized(const std::string& user_id, + const LLUUID &agentID) override; ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceClientStatusObserver* observer); - virtual void removeObserver(LLVoiceClientStatusObserver* observer); - virtual void addObserver(LLFriendObserver* observer); - virtual void removeObserver(LLFriendObserver* observer); - virtual void addObserver(LLVoiceClientParticipantObserver* observer); - virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + 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); + std::string sipURIFromID(const LLUUID &id) override; //@} /// @name LLVoiceEffectInterface virtual implementations @@ -221,20 +221,20 @@ public: ////////////////////////// /// @name Accessors //@{ - virtual bool setVoiceEffect(const LLUUID& id); - virtual const LLUUID getVoiceEffect(); - virtual LLSD getVoiceEffectProperties(const LLUUID& id); + bool setVoiceEffect(const LLUUID& id) override; + const LLUUID getVoiceEffect() override; + LLSD getVoiceEffectProperties(const LLUUID& id) override; - virtual void refreshVoiceEffectLists(bool clear_lists); - virtual const voice_effect_list_t& getVoiceEffectList() const; - virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + 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); - virtual void removeObserver(LLVoiceEffectObserver* observer); + void addObserver(LLVoiceEffectObserver* observer) override; + void removeObserver(LLVoiceEffectObserver* observer) override; //@} ////////////////////////////// @@ -263,13 +263,13 @@ public: ////////////////////////////// /// @name Effect preview buffer //@{ - virtual void enablePreviewBuffer(bool enable); - virtual void recordPreviewBuffer(); - virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); - virtual void stopPreviewBuffer(); + void enablePreviewBuffer(bool enable) override; + void recordPreviewBuffer() override; + void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; + void stopPreviewBuffer() override; - virtual bool isPreviewRecording(); - virtual bool isPreviewPlaying(); + bool isPreviewRecording() override; + bool isPreviewPlaying() override; //@} //@} @@ -773,7 +773,7 @@ private: std::string getAudioSessionURI(); std::string getAudioSessionHandle(); - void setHidden(bool hidden); //virtual + void setHidden(bool hidden) override; //virtual void sendPositionAndVolumeUpdate(void); void sendFriendsListUpdates(); diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 1fa4df1682..9230cb0589 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'): @@ -990,6 +996,14 @@ 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) + # 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. -- cgit v1.2.3 From 3bf3562f9c8ebe33f1f18ef39430307aae304c5d Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 9 Sep 2023 23:52:34 -0700 Subject: Use webrtc built binaries from temporary s3 location --- indra/cmake/WebRTC.cmake | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 2040c86b9f..c1e5df8b9d 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -3,23 +3,34 @@ include(FetchContent) if (WINDOWS) -FetchContent_Declare( - webrtc - URL http://localhost:8000/webrtc.windows_x86_64.tar.bz2 - URL_HASH MD5=dfb692562770dc8c877ebfe4302e2881 - FIND_PACKAGE_ARGS NAMES webrtc - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" -) + if( ADDRESS_SIZE EQUAL 32 ) + FetchContent_Declare( + webrtc + URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2 + URL_HASH MD5=0d55e58efceed3fb48085a5f0c58881c + FIND_PACKAGE_ARGS NAMES webrtc + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + ) + else ( ADDRESS_SIZE EQUAL 32 ) + FetchContent_Declare( + webrtc + URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2 + URL_HASH MD5=dfb692562770dc8c877ebfe4302e2881 + FIND_PACKAGE_ARGS NAMES webrtc + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + ) + endif ( ADDRESS_SIZE EQUAL 32 ) elseif (DARWIN) -FetchContent_Declare( - webrtc - URL http://localhost:8000/webrtc.macos_x86_64.tar.bz2 - URL_HASH MD5=cfbcac7da897a862f9791ea29330b814 - FIND_PACKAGE_ARGS NAMES webrtc - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" -) + FetchContent_Declare( + webrtc + URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2 + URL_HASH MD5=cfbcac7da897a862f9791ea29330b814 + FIND_PACKAGE_ARGS NAMES webrtc + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + ) endif (WINDOWS) FetchContent_MakeAvailable(webrtc) -- cgit v1.2.3 From 781a7aabc10d21a951ac86b75df7ca7565c45039 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 00:30:59 -0700 Subject: coding policy fixes --- indra/cmake/LLWebRTC.cmake | 1 - indra/llwebrtc/llwebrtc.cpp | 2 +- indra/llwebrtc/llwebrtc.h | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 indra/cmake/LLWebRTC.cmake (limited to 'indra') diff --git a/indra/cmake/LLWebRTC.cmake b/indra/cmake/LLWebRTC.cmake deleted file mode 100644 index 913e28c2ff..0000000000 --- a/indra/cmake/LLWebRTC.cmake +++ /dev/null @@ -1 +0,0 @@ -# -*- cmake -*- \ No newline at end of file diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 5e71a00b60..e7a9072b2e 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -483,4 +483,4 @@ void init() gWebRTCImpl->AddRef(); gWebRTCImpl->init(); } -} // namespace llwebrtc \ No newline at end of file +} // namespace llwebrtc diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 121efaab86..cb639ee116 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -124,4 +124,4 @@ LLSYMEXPORT LLWebRTCDeviceInterface* getDeviceInterface(); LLSYMEXPORT LLWebRTCSignalInterface* getSignalingInterface(); } -#endif // LLWEBRTC_H \ No newline at end of file +#endif // LLWEBRTC_H -- cgit v1.2.3 From 6db4beaefdf342ad8b657f9c9d75f8e1d842daf1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 00:39:58 -0700 Subject: remove unnecessary build file --- indra/newview/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6d9b0ab2dc..c804f5403c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -30,7 +30,6 @@ include(LLKDU) include(LLPhysicsExtensions) include(LLPrimitive) include(LLWindow) -include(LLWebRTC) include(NDOF) include(NVAPI) include(OPENAL) -- cgit v1.2.3 From 71591655523743e6c34949543bf6c1f7ae1d9df0 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 00:50:22 -0700 Subject: unknown warnings for TC build machines --- indra/cmake/00-Common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 687ace431b..897eabb233 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -182,7 +182,7 @@ if (LINUX OR DARWIN) list(APPEND GCC_WARNINGS -Wno-reorder -Wno-non-virtual-dtor ) - list(APPEND GCC_WARNINGS -Wno-unused-but-set-variable -Wno-unused-variable ) +# list(APPEND GCC_WARNINGS -Wno-unused-but-set-variable -Wno-unused-variable ) add_compile_options(${GCC_WARNINGS}) add_compile_options(-m${ADDRESS_SIZE}) -- cgit v1.2.3 From 49d55fb10d92e1f44fb2ad0ddf832b5b0cff433a Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 01:42:27 -0700 Subject: TC build cmake complained about URL_HASH --- indra/cmake/WebRTC.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index c1e5df8b9d..fc664b75bf 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -7,7 +7,7 @@ if (WINDOWS) FetchContent_Declare( webrtc URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2 - URL_HASH MD5=0d55e58efceed3fb48085a5f0c58881c + URL_HASH "MD5=0d55e58efceed3fb48085a5f0c58881c" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" @@ -16,7 +16,7 @@ if (WINDOWS) FetchContent_Declare( webrtc URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2 - URL_HASH MD5=dfb692562770dc8c877ebfe4302e2881 + URL_HASH "MD5=dfb692562770dc8c877ebfe4302e2881" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" @@ -26,7 +26,7 @@ elseif (DARWIN) FetchContent_Declare( webrtc URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2 - URL_HASH MD5=cfbcac7da897a862f9791ea29330b814 + URL_HASH "MD5=cfbcac7da897a862f9791ea29330b814" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" -- cgit v1.2.3 From 38823ad0af830bd6040f81f74d29fb6bd02749b1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 01:50:07 -0700 Subject: Try reordering URL_HASH --- indra/cmake/WebRTC.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index fc664b75bf..7693899247 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -7,29 +7,29 @@ if (WINDOWS) FetchContent_Declare( webrtc URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2 - URL_HASH "MD5=0d55e58efceed3fb48085a5f0c58881c" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + URL_HASH "MD5=0d55e58efceed3fb48085a5f0c58881c" ) else ( ADDRESS_SIZE EQUAL 32 ) FetchContent_Declare( webrtc URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2 - URL_HASH "MD5=dfb692562770dc8c877ebfe4302e2881" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + URL_HASH "MD5=dfb692562770dc8c877ebfe4302e2881" ) endif ( ADDRESS_SIZE EQUAL 32 ) elseif (DARWIN) FetchContent_Declare( webrtc URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2 - URL_HASH "MD5=cfbcac7da897a862f9791ea29330b814" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + URL_HASH "MD5=cfbcac7da897a862f9791ea29330b814" ) endif (WINDOWS) -- cgit v1.2.3 From 3ef34cf7b643d04c31074714bf8228a9ebe67ac5 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 01:56:43 -0700 Subject: Cmake on TC is sure picky --- indra/cmake/WebRTC.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 7693899247..64c41c59f8 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -6,7 +6,7 @@ if (WINDOWS) if( ADDRESS_SIZE EQUAL 32 ) FetchContent_Declare( webrtc - URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2 + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" @@ -15,7 +15,7 @@ if (WINDOWS) else ( ADDRESS_SIZE EQUAL 32 ) FetchContent_Declare( webrtc - URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2 + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" @@ -25,7 +25,7 @@ if (WINDOWS) elseif (DARWIN) FetchContent_Declare( webrtc - URL https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2 + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" -- cgit v1.2.3 From 007b2127b81168071fe44c1a91961ae75487c0bc Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 02:03:30 -0700 Subject: more picky cmake --- indra/cmake/WebRTC.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 64c41c59f8..4b2dcebbe6 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -6,29 +6,29 @@ if (WINDOWS) if( ADDRESS_SIZE EQUAL 32 ) FetchContent_Declare( webrtc - URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2" URL_HASH "MD5=0d55e58efceed3fb48085a5f0c58881c" ) else ( ADDRESS_SIZE EQUAL 32 ) FetchContent_Declare( webrtc - URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2" URL_HASH "MD5=dfb692562770dc8c877ebfe4302e2881" ) endif ( ADDRESS_SIZE EQUAL 32 ) elseif (DARWIN) FetchContent_Declare( webrtc - URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2" URL_HASH "MD5=cfbcac7da897a862f9791ea29330b814" ) endif (WINDOWS) -- cgit v1.2.3 From 0cb1413d60dde3b975bf2ce541b17baf0927d8e0 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 11:19:08 -0700 Subject: set toolset for llwebrtc --- indra/llwebrtc/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt index fde4b87a3e..a06e0aaa03 100644 --- a/indra/llwebrtc/CMakeLists.txt +++ b/indra/llwebrtc/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -#set(CMAKE_GENERATOR_TOOLSET "clang-cl") +set(CMAKE_GENERATOR_TOOLSET "CLangCL") include(00-Common) include(Linking) @@ -52,6 +52,10 @@ target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) if (WINDOWS) set_property(TARGET llwebrtc PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") + ADD_CUSTOM_COMMAND(TARGET llwebrtc POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + ${SHARED_LIB_STAGING_DIR}) endif (WINDOWS) ADD_CUSTOM_COMMAND(TARGET llwebrtc POST_BUILD -- cgit v1.2.3 From b2d4ce16ad344162ecc190f798704e3ac179555a Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 19:24:03 -0700 Subject: Fix windows pragma error --- indra/llwebrtc/llwebrtc_impl.h | 1 + 1 file changed, 1 insertion(+) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 07c0f514b2..d439bd253d 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -41,6 +41,7 @@ // WebRTC Includes #ifdef WEBRTC_WIN #pragma warning(disable : 4996) +#pragma warning(disable : 4068) #endif // WEBRTC_WIN #include "api/scoped_refptr.h" -- cgit v1.2.3 From ebfcb6f4e8cad8052ba2c924741a0098b572ed81 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 11 Sep 2023 15:38:58 -0700 Subject: some build tweaks --- indra/cmake/WebRTC.cmake | 4 ++-- indra/llwebrtc/CMakeLists.txt | 8 +++----- indra/newview/viewer_manifest.py | 6 ++++++ indra/secondlife-binRelWithDebInfo.entitlements | 8 ++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 indra/secondlife-binRelWithDebInfo.entitlements (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 4b2dcebbe6..20417ebb41 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -25,11 +25,11 @@ if (WINDOWS) elseif (DARWIN) FetchContent_Declare( webrtc + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2" + URL_HASH "MD5=cfbcac7da897a862f9791ea29330b814" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" - URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2" - URL_HASH "MD5=cfbcac7da897a862f9791ea29330b814" ) endif (WINDOWS) diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt index a06e0aaa03..f32d3dc580 100644 --- a/indra/llwebrtc/CMakeLists.txt +++ b/indra/llwebrtc/CMakeLists.txt @@ -5,7 +5,9 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_GENERATOR_TOOLSET "CLangCL") +if (WINDOWS) + set(CMAKE_GENERATOR_TOOLSET "CLangCL") +endif (WINDOWS) include(00-Common) include(Linking) @@ -52,10 +54,6 @@ target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) if (WINDOWS) set_property(TARGET llwebrtc PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") - ADD_CUSTOM_COMMAND(TARGET llwebrtc POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - ${SHARED_LIB_STAGING_DIR}) endif (WINDOWS) ADD_CUSTOM_COMMAND(TARGET llwebrtc POST_BUILD diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 9230cb0589..b7b737ee4b 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1004,6 +1004,12 @@ class Darwin_x86_64_Manifest(ViewerManifest): ): 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/indra/secondlife-binRelWithDebInfo.entitlements b/indra/secondlife-binRelWithDebInfo.entitlements new file mode 100644 index 0000000000..b572d9c04e --- /dev/null +++ b/indra/secondlife-binRelWithDebInfo.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.device.audio-input + + + -- cgit v1.2.3 From 6d81e64348009c1bb656fe500e9a08ab7f739311 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 11 Sep 2023 15:39:49 -0700 Subject: inadvertantly added --- indra/secondlife-binRelWithDebInfo.entitlements | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 indra/secondlife-binRelWithDebInfo.entitlements (limited to 'indra') diff --git a/indra/secondlife-binRelWithDebInfo.entitlements b/indra/secondlife-binRelWithDebInfo.entitlements deleted file mode 100644 index b572d9c04e..0000000000 --- a/indra/secondlife-binRelWithDebInfo.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.device.audio-input - - - -- cgit v1.2.3 From a974a1517901eb0a93099853a89bf55904737cec Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 12 Sep 2023 15:17:08 -0700 Subject: do some thread safety to prevent webrtc threads from conflicting with viewer threads. --- indra/llwebrtc/llwebrtc.cpp | 35 +++++++++-- indra/llwebrtc/llwebrtc.h | 1 + indra/newview/llvoicewebrtc.cpp | 129 +++++++++++++++++++++++++--------------- indra/newview/llvoicewebrtc.h | 13 ++++ 4 files changed, 126 insertions(+), 52 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index e7a9072b2e..c2631b2ea3 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -389,6 +389,36 @@ void LLWebRTCImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGath void LLWebRTCImpl::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: + { + if (new_state == webrtc::PeerConnectionInterface::PeerConnectionState::kConnected) + { + for (auto &observer : mSignalingObserverList) + { + observer->OnAudioEstablished(this); + } + } + break; + } + case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: + { + if (new_state == webrtc::PeerConnectionInterface::PeerConnectionState::kConnected) + { + for (auto &observer : mSignalingObserverList) + { + observer->OnRenegotiationNeeded(); + } + } + break; + } + default: + { + break; + } + } } void LLWebRTCImpl::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) @@ -447,10 +477,6 @@ void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); return; } - for (auto &observer : mSignalingObserverList) - { - observer->OnAudioEstablished(this); - } } // @@ -467,6 +493,7 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) auto desc = mPeerConnection->pending_local_description(); std::string sdp; desc->ToString(&sdp); + RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp; for (auto &observer : mSignalingObserverList) { observer->OnOfferAvailable(sdp); diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index cb639ee116..acc3665e95 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -106,6 +106,7 @@ class LLWebRTCSignalingObserver virtual void OnIceGatheringState(IceGatheringState state) = 0; virtual void OnIceCandidate(const LLWebRTCIceCandidate& candidate) = 0; virtual void OnOfferAvailable(const std::string& sdp) = 0; + virtual void OnRenegotiationNeeded() = 0; virtual void OnAudioEstablished(LLWebRTCAudioInterface *audio_interface) = 0; }; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 5fcfc969b5..d79b11f1da 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -603,7 +603,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() U32 retry = 0; - mVoiceControlState = VOICE_STATE_TP_WAIT; + setVoiceControlState(VOICE_STATE_TP_WAIT); do { @@ -617,7 +617,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() processIceUpdates(); - switch (mVoiceControlState) + switch (getVoiceControlState()) { case VOICE_STATE_TP_WAIT: // starting point for voice @@ -628,37 +628,44 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { - mVoiceControlState = VOICE_STATE_START_SESSION; + setVoiceControlState(VOICE_STATE_START_SESSION); } break; case VOICE_STATE_START_SESSION: if (establishVoiceConnection()) { - mVoiceControlState = VOICE_STATE_WAIT_FOR_SESSION_START; + setVoiceControlState(VOICE_STATE_WAIT_FOR_SESSION_START); } else { - mVoiceControlState = VOICE_STATE_SESSION_RETRY; + setVoiceControlState(VOICE_STATE_SESSION_RETRY); } break; case VOICE_STATE_WAIT_FOR_SESSION_START: - llcoro::suspendUntilTimeout(1.0); - if (!mChannelSDP.empty()) { - mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; + llcoro::suspendUntilTimeout(1.0); + std::string channel_sdp; + { + LLMutexLock lock(&mVoiceStateMutex); + channel_sdp = mChannelSDP; + } + if (!channel_sdp.empty() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) + { + setVoiceControlState(VOICE_STATE_PROVISION_ACCOUNT); + } + break; } - break; - case VOICE_STATE_PROVISION_ACCOUNT: - if (!provisionVoiceAccount()) + // getVoiceControlState() can change while provisionVoiceAccount is happening + if (!provisionVoiceAccount() && getVoiceControlState() == VOICE_STATE_SESSION_RETRY) { - mVoiceControlState = VOICE_STATE_SESSION_RETRY; + setVoiceControlState(VOICE_STATE_SESSION_RETRY); } else { - mVoiceControlState = VOICE_STATE_SESSION_PROVISION_WAIT; + setVoiceControlState(VOICE_STATE_SESSION_PROVISION_WAIT); } break; case VOICE_STATE_SESSION_PROVISION_WAIT: @@ -685,11 +692,11 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() current_delay++; llcoro::suspendUntilTimeout(1.f); } - mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); } else { - mVoiceControlState = VOICE_STATE_DONE; + setVoiceControlState(VOICE_STATE_DONE); } break; @@ -700,38 +707,43 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() performMicTuning(); } - mVoiceControlState = VOICE_STATE_WAIT_FOR_CHANNEL; + setVoiceControlState(VOICE_STATE_WAIT_FOR_CHANNEL); } break; case VOICE_STATE_WAIT_FOR_CHANNEL: - waitForChannel(); // todo: split into more states like login/fonts - mVoiceControlState = VOICE_STATE_DISCONNECT; + { + if (!waitForChannel()) // todo: split into more states like login/fonts + { + setVoiceControlState(VOICE_STATE_DISCONNECT); + } + // on true, it's a retry, so let the state stand. + } break; case VOICE_STATE_DISCONNECT: LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; endAndDisconnectSession(); retry = 0; // Connected without issues - mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); break; case VOICE_STATE_WAIT_FOR_EXIT: if (mRelogRequested && mVoiceEnabled) { LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - mVoiceControlState = VOICE_STATE_TP_WAIT; + setVoiceControlState(VOICE_STATE_TP_WAIT); } else { - mVoiceControlState = VOICE_STATE_DONE; + setVoiceControlState(VOICE_STATE_DONE); } break; case VOICE_STATE_DONE: break; } - } while (mVoiceControlState > 0); + } while (getVoiceControlState() > 0); if (sShuttingDown) { @@ -792,7 +804,10 @@ bool LLWebRTCVoiceClient::provisionVoiceAccount() LLSD body; LLSD jsep; jsep["type"] = "offer"; - jsep["sdp"] = mChannelSDP; + { + LLMutexLock lock(&mVoiceStateMutex); + jsep["sdp"] = mChannelSDP; + } body["jsep"] = jsep; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( @@ -806,7 +821,6 @@ bool LLWebRTCVoiceClient::provisionVoiceAccount() void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) { - mVoiceControlState = VOICE_STATE_SESSION_ESTABLISHED; LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); std::string channelSDP; if (result.has("jsep") && @@ -825,6 +839,7 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; setLoginInfo(voiceUserName, voicePassword, channelSDP); + setVoiceControlState(VOICE_STATE_SESSION_ESTABLISHED); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -1326,6 +1341,11 @@ bool LLWebRTCVoiceClient::waitForChannel() return false; } + if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) + { + return true; + } + processIceUpdates(); switch (state) { @@ -2759,14 +2779,16 @@ void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserve { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - if (state != llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) - { - return; + if (state == llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) + { + LLMutexLock lock(&mVoiceStateMutex); + mIceCompleted = true; } - mIceCompleted = true; } -void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { +void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +{ + LLMutexLock lock(&mVoiceStateMutex); mIceCandidates.push_back(candidate); } @@ -2795,31 +2817,35 @@ void LLWebRTCVoiceClient::processIceUpdates() LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); LLSD body; - if (mIceCandidates.size()) { - LLSD body; + LLMutexLock lock(&mVoiceStateMutex); - for (auto &ice_candidate : mIceCandidates) + if (mIceCandidates.size()) + { + LLSD body; + + for (auto &ice_candidate : mIceCandidates) + { + LLSD body_candidate; + body_candidate["sdpMid"] = ice_candidate.sdp_mid; + body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; + body_candidate["candidate"] = ice_candidate.candidate; + body["candidates"].append(body_candidate); + } + mIceCandidates.clear(); + } + else if (mIceCompleted) { LLSD body_candidate; - body_candidate["sdpMid"] = ice_candidate.sdp_mid; - body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; - body_candidate["candidate"] = ice_candidate.candidate; - body["candidates"].append(body_candidate); + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + mIceCompleted = false; + } + else + { + return; } - mIceCandidates.clear(); - } - else if (mIceCompleted) - { - LLSD body_candidate; - body_candidate["completed"] = true; - body["candidate"] = body_candidate; - mIceCompleted = false; } - else - { - return; - } LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, @@ -2862,6 +2888,7 @@ void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD bo void LLWebRTCVoiceClient::OnOfferAvailable(const std::string &sdp) { LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; + LLMutexLock lock(&mVoiceStateMutex); mChannelSDP = sdp; } @@ -2872,6 +2899,12 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * audio_interface->setMute(true); } +void LLWebRTCVoiceClient::OnRenegotiationNeeded() +{ + LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; + setVoiceControlState(VOICE_STATE_SESSION_RETRY); +} + ///////////////////////////// // Response/Event handlers diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 22c022ffdb..061e00581e 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -252,6 +252,7 @@ public: void OnIceGatheringState(IceGatheringState 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; //@} @@ -651,7 +652,19 @@ private: //--- void voiceControlCoro(); void voiceControlStateMachine(); + int mVoiceControlState; + LLMutex mVoiceStateMutex; + void setVoiceControlState(int new_voice_control_state) + { + LLMutexLock lock(&mVoiceStateMutex); + mVoiceControlState = new_voice_control_state; + } + int getVoiceControlState() + { + LLMutexLock lock(&mVoiceStateMutex); + return mVoiceControlState; + } bool endAndDisconnectSession(); -- cgit v1.2.3 From 5f267412f6f110a87583b547d27744d8760fc2ef Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 11:51:36 -0700 Subject: Add some thread safety --- indra/newview/llvoicewebrtc.cpp | 48 +++++++++++++++++++++-------------------- indra/newview/llvoicewebrtc.h | 7 ++++-- 2 files changed, 30 insertions(+), 25 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d79b11f1da..516bab1914 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -603,7 +603,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() U32 retry = 0; - setVoiceControlState(VOICE_STATE_TP_WAIT); + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT, VOICE_STATE_SESSION_RETRY); do { @@ -628,18 +628,18 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { - setVoiceControlState(VOICE_STATE_START_SESSION); + setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); } break; case VOICE_STATE_START_SESSION: - if (establishVoiceConnection()) + if (establishVoiceConnection() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) { - setVoiceControlState(VOICE_STATE_WAIT_FOR_SESSION_START); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_SESSION_START, VOICE_STATE_SESSION_RETRY); } else { - setVoiceControlState(VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } break; @@ -649,23 +649,25 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() std::string channel_sdp; { LLMutexLock lock(&mVoiceStateMutex); - channel_sdp = mChannelSDP; - } - if (!channel_sdp.empty() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) - { - setVoiceControlState(VOICE_STATE_PROVISION_ACCOUNT); + if (mVoiceControlState == VOICE_STATE_SESSION_RETRY) + { + break; + } + if (!mChannelSDP.empty()) + { + mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; + } } break; } case VOICE_STATE_PROVISION_ACCOUNT: - // getVoiceControlState() can change while provisionVoiceAccount is happening - if (!provisionVoiceAccount() && getVoiceControlState() == VOICE_STATE_SESSION_RETRY) + if (!provisionVoiceAccount()) { - setVoiceControlState(VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } else { - setVoiceControlState(VOICE_STATE_SESSION_PROVISION_WAIT); + setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); } break; case VOICE_STATE_SESSION_PROVISION_WAIT: @@ -692,11 +694,11 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() current_delay++; llcoro::suspendUntilTimeout(1.f); } - setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); } else { - setVoiceControlState(VOICE_STATE_DONE); + setVoiceControlStateUnless(VOICE_STATE_DONE); } break; @@ -707,7 +709,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() performMicTuning(); } - setVoiceControlState(VOICE_STATE_WAIT_FOR_CHANNEL); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_CHANNEL, VOICE_STATE_SESSION_RETRY); } break; @@ -715,7 +717,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() { if (!waitForChannel()) // todo: split into more states like login/fonts { - setVoiceControlState(VOICE_STATE_DISCONNECT); + setVoiceControlStateUnless(VOICE_STATE_DISCONNECT, VOICE_STATE_SESSION_RETRY); } // on true, it's a retry, so let the state stand. } @@ -725,18 +727,18 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; endAndDisconnectSession(); retry = 0; // Connected without issues - setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); break; case VOICE_STATE_WAIT_FOR_EXIT: if (mRelogRequested && mVoiceEnabled) { LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - setVoiceControlState(VOICE_STATE_TP_WAIT); + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); } else { - setVoiceControlState(VOICE_STATE_DONE); + setVoiceControlStateUnless(VOICE_STATE_DONE); } break; @@ -839,7 +841,7 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; setLoginInfo(voiceUserName, voicePassword, channelSDP); - setVoiceControlState(VOICE_STATE_SESSION_ESTABLISHED); + setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -2902,7 +2904,7 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * void LLWebRTCVoiceClient::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; - setVoiceControlState(VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } ///////////////////////////// diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 061e00581e..f4e2ac0b7f 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -655,10 +655,13 @@ private: int mVoiceControlState; LLMutex mVoiceStateMutex; - void setVoiceControlState(int new_voice_control_state) + void setVoiceControlStateUnless(int new_voice_control_state, int unless=-1) { LLMutexLock lock(&mVoiceStateMutex); - mVoiceControlState = new_voice_control_state; + if (mVoiceControlState != unless) + { + mVoiceControlState = new_voice_control_state; + } } int getVoiceControlState() { -- cgit v1.2.3 From 8ba90d077381bf16b4ba03a0f530f76e770e69c1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 14:49:42 -0700 Subject: Hook up speaker volume. --- indra/llwebrtc/llwebrtc.cpp | 22 ++ indra/llwebrtc/llwebrtc.h | 1 + indra/llwebrtc/llwebrtc_impl.h | 1 + indra/newview/llvoicewebrtc.cpp | 797 ++-------------------------------------- indra/newview/llvoicewebrtc.h | 114 ------ 5 files changed, 49 insertions(+), 886 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index c2631b2ea3..93e9db9c1d 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -341,6 +341,28 @@ void LLWebRTCImpl::setMute(bool mute) }); } +void LLWebRTCImpl::setSpeakerVolume(float volume) +{ + mSignalingThread->PostTask( + [this, volume]() + { + auto receivers = mPeerConnection->GetReceivers(); + + RTC_LOG(LS_INFO) << __FUNCTION__ << "Set volume" << receivers.size(); + for (auto &receiver : receivers) + { + webrtc::MediaStreamTrackInterface *track = receiver->track().get(); + if (track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) + { + webrtc::AudioTrackInterface* audio_track = static_cast(track); + webrtc::AudioSourceInterface* source = audio_track->GetSource(); + source->SetVolume(10.0 * volume); + + } + } + }); +} + // // PeerConnectionObserver implementation. // diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index acc3665e95..ca558add01 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -93,6 +93,7 @@ class LLWebRTCAudioInterface { public: virtual void setMute(bool mute) = 0; + virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 }; class LLWebRTCSignalingObserver diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index d439bd253d..5c6cfcdbc6 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -125,6 +125,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // LLWebRTCAudioInterface // void setMute(bool mute) override; + void setSpeakerVolume(float folume) override; // range 0.0-1.0 // // PeerConnectionObserver implementation. diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 516bab1914..23033c5fee 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -123,14 +123,6 @@ static int scale_mic_volume(float volume) 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 WebRTC levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 - return 30 + (int)(volume * 40.0f); - -} - /////////////////////////////////////////////////////////////////////////////////////////////// @@ -317,7 +309,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : sConnected = false; sPump = nullptr; - mSpeakerVolume = scale_speaker_volume(0); + mSpeakerVolume = 0.0; mVoiceVersion.serverVersion = ""; mVoiceVersion.serverType = VOICE_SERVER_TYPE; @@ -2220,12 +2212,11 @@ void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) -{ - int scaled_volume = scale_speaker_volume(volume); +{ - if(scaled_volume != mTuningSpeakerVolume) + if (volume != mTuningSpeakerVolume) { - mTuningSpeakerVolume = scaled_volume; + mTuningSpeakerVolume = volume; mTuningSpeakerVolumeDirty = true; } } @@ -2663,7 +2654,7 @@ void LLWebRTCVoiceClient::sendLocalAudioUpdates() if (mSpeakerMuteDirty && !mTuningMode) { - const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false"); + const char *muteval = ((mSpeakerVolume <= 0.0) ? "true" : "false"); mSpeakerMuteDirty = false; @@ -2899,6 +2890,11 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; audio_interface->setMute(true); + { + LLMutexLock lock(&mVoiceStateMutex); + + audio_interface->setSpeakerVolume(mSpeakerVolume); + } } void LLWebRTCVoiceClient::OnRenegotiationNeeded() @@ -4901,18 +4897,23 @@ void LLWebRTCVoiceClient::setEarLocation(S32 loc) void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) { - int scaled_volume = scale_speaker_volume(volume); - - if(scaled_volume != mSpeakerVolume) + if (volume != mSpeakerVolume) { - int min_volume = scale_speaker_volume(0); - if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) - { - mSpeakerMuteDirty = true; - } + { + LLMutexLock lock(&mVoiceStateMutex); + int min_volume = 0.0; + if ((volume == min_volume) || (mSpeakerVolume == min_volume)) + { + mSpeakerMuteDirty = true; + } - mSpeakerVolume = scaled_volume; - mSpeakerVolumeDirty = true; + mSpeakerVolume = volume; + mSpeakerVolumeDirty = true; + } + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setSpeakerVolume(volume); + } } } @@ -6546,755 +6547,7 @@ void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() } } -LLWebRTCProtocolParser::LLWebRTCProtocolParser() -{ - parser = XML_ParserCreate(NULL); - - reset(); -} - -void LLWebRTCProtocolParser::reset() -{ - responseDepth = 0; - ignoringTags = false; - accumulateText = false; - energy = 0.f; - hasText = false; - hasAudio = false; - hasVideo = false; - terminated = false; - ignoreDepth = 0; - isChannel = false; - incoming = false; - enabled = false; - isEvent = false; - isLocallyMuted = false; - isModeratorMuted = false; - isSpeaking = false; - participantType = 0; - returnCode = -1; - state = 0; - statusCode = 0; - volume = 0; - textBuffer.clear(); - alias.clear(); - numberOfAliases = 0; - applicationString.clear(); -} - -//virtual -LLWebRTCProtocolParser::~LLWebRTCProtocolParser() -{ - if (parser) - XML_ParserFree(parser); -} - -static LLTrace::BlockTimerStatHandle FTM_WebRTC_PROCESS("WebRTC Process"); - -// virtual -LLIOPipe::EStatus LLWebRTCProtocolParser::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_WebRTC_PROCESS); - LLBufferStream istr(channels, buffer.get()); - std::ostringstream ostr; - while (istr.good()) - { - char buf[1024]; - istr.read(buf, sizeof(buf)); - mInput.append(buf, istr.gcount()); - } - - // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. - int start = 0; - int delim; - while((delim = mInput.find("\n\n\n", start)) != std::string::npos) - { - - // Reset internal state of the LLWebRTCProtocolParser (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_Parse(parser, mInput.data() + start, delim - start, false); - - LL_DEBUGS("WebRTCProtocolParser") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; - start = delim + 3; - } - - if(start != 0) - mInput = mInput.substr(start); - - LL_DEBUGS("WebRTCProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; - - if(!LLWebRTCVoiceClient::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; -} - -void XMLCALL LLWebRTCProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) -{ - if (data) - { - LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; - object->StartTag(el, attr); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLWebRTCProtocolParser::ExpatEndTag(void *data, const char *el) -{ - if (data) - { - LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; - object->EndTag(el); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLWebRTCProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) -{ - if (data) - { - LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; - object->CharData(s, len); - } -} - -// -------------------------------------------------------------------------------- - - -void LLWebRTCProtocolParser::StartTag(const char *tag, const char **attr) -{ - // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags - 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 - while (*attr) - { - const char *key = *attr++; - const char *value = *attr++; - - if (!stricmp("requestId", key)) - { - requestId = value; - } - else if (!stricmp("action", key)) - { - actionString = value; - } - else if (!stricmp("type", key)) - { - eventTypeString = value; - } - } - } - LL_DEBUGS("WebRTCProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; - } - else - { - if (ignoringTags) - { - LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - else - { - LL_DEBUGS("WebRTCProtocolParser") << 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("WebRTCProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; - } - else if (!stricmp("CaptureDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->clearCaptureDevices(); - } - else if (!stricmp("RenderDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->clearRenderDevices(); - } - else if (!stricmp("CaptureDevice", tag)) - { - deviceString.clear(); - } - else if (!stricmp("RenderDevice", tag)) - { - deviceString.clear(); - } - else if (!stricmp("SessionFont", tag)) - { - id = 0; - nameString.clear(); - descriptionString.clear(); - expirationDate = LLDate(); - hasExpired = false; - fontType = 0; - fontStatus = 0; - } - else if (!stricmp("TemplateFont", tag)) - { - id = 0; - nameString.clear(); - descriptionString.clear(); - expirationDate = LLDate(); - hasExpired = false; - fontType = 0; - fontStatus = 0; - } - else if (!stricmp("MediaCompletionType", tag)) - { - mediaCompletionType.clear(); - } - } - } - responseDepth++; -} - -// -------------------------------------------------------------------------------- - -void LLWebRTCProtocolParser::EndTag(const char *tag) -{ - const std::string& string = textBuffer; - - responseDepth--; - - if (ignoringTags) - { - if (ignoreDepth == responseDepth) - { - LL_DEBUGS("WebRTCProtocolParser") << "end of ignore" << LL_ENDL; - ignoringTags = false; - } - else - { - LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - } - - if (!ignoringTags) - { - LL_DEBUGS("WebRTCProtocolParser") << "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); - else if (!stricmp("SessionHandle", tag)) - sessionHandle = string; - else if (!stricmp("SessionGroupHandle", tag)) - sessionGroupHandle = string; - else if (!stricmp("StatusCode", tag)) - statusCode = strtol(string.c_str(), NULL, 10); - else if (!stricmp("StatusString", tag)) - statusString = string; - else if (!stricmp("ParticipantURI", tag)) - uriString = string; - else if (!stricmp("Volume", tag)) - volume = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Energy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("IsModeratorMuted", tag)) - isModeratorMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("IsSpeaking", tag)) - isSpeaking = !stricmp(string.c_str(), "true"); - else if (!stricmp("Alias", tag)) - alias = string; - else if (!stricmp("NumberOfAliases", tag)) - numberOfAliases = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Application", tag)) - applicationString = string; - else if (!stricmp("ConnectorHandle", tag)) - connectorHandle = string; - else if (!stricmp("VersionID", tag)) - versionID = string; - else if (!stricmp("Version", tag)) - mBuildID = string; - else if (!stricmp("AccountHandle", tag)) - accountHandle = string; - else if (!stricmp("State", tag)) - state = strtol(string.c_str(), NULL, 10); - else if (!stricmp("URI", tag)) - uriString = string; - else if (!stricmp("IsChannel", tag)) - isChannel = !stricmp(string.c_str(), "true"); - else if (!stricmp("Incoming", tag)) - incoming = !stricmp(string.c_str(), "true"); - else if (!stricmp("Enabled", tag)) - enabled = !stricmp(string.c_str(), "true"); - else if (!stricmp("Name", tag)) - nameString = string; - else if (!stricmp("AudioMedia", tag)) - audioMediaString = string; - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("DisplayName", tag)) - displayNameString = string; - else if (!stricmp("Device", tag)) - deviceString = string; - else if (!stricmp("AccountName", tag)) - nameString = string; - else if (!stricmp("ParticipantType", tag)) - participantType = strtol(string.c_str(), NULL, 10); - else if (!stricmp("IsLocallyMuted", tag)) - isLocallyMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("MicEnergy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("ChannelURI", tag)) - uriString = string; - else if (!stricmp("BuddyURI", tag)) - uriString = string; - else if (!stricmp("Presence", tag)) - statusString = string; - else if (!stricmp("CaptureDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); - } - else if (!stricmp("RenderDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); - } - else if (!stricmp("CaptureDevice", tag)) - { - LLWebRTCVoiceClient::getInstance()->addCaptureDevice(LLVoiceDevice(displayNameString, deviceString)); - } - else if (!stricmp("RenderDevice", tag)) - { - LLWebRTCVoiceClient::getInstance()->addRenderDevice(LLVoiceDevice(displayNameString, deviceString)); - } - else if (!stricmp("BlockMask", tag)) - blockMask = string; - else if (!stricmp("PresenceOnly", tag)) - presenceOnly = string; - else if (!stricmp("AutoAcceptMask", tag)) - autoAcceptMask = string; - else if (!stricmp("AutoAddAsBuddy", tag)) - autoAddAsBuddy = string; - else if (!stricmp("MessageHeader", tag)) - messageHeader = string; - else if (!stricmp("MessageBody", tag)) - messageBody = string; - else if (!stricmp("NotificationType", tag)) - notificationType = string; - else if (!stricmp("HasText", tag)) - hasText = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasAudio", tag)) - hasAudio = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasVideo", tag)) - hasVideo = !stricmp(string.c_str(), "true"); - else if (!stricmp("Terminated", tag)) - terminated = !stricmp(string.c_str(), "true"); - else if (!stricmp("SubscriptionHandle", tag)) - subscriptionHandle = string; - else if (!stricmp("SubscriptionType", tag)) - subscriptionType = string; - else if (!stricmp("SessionFont", tag)) - { - LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, false); - } - else if (!stricmp("TemplateFont", tag)) - { - LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, true); - } - else if (!stricmp("ID", tag)) - { - id = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("Description", tag)) - { - descriptionString = string; - } - else if (!stricmp("ExpirationDate", tag)) - { - expirationDate = expiryTimeStampToLLDate(string); - } - else if (!stricmp("Expired", tag)) - { - hasExpired = !stricmp(string.c_str(), "1"); - } - else if (!stricmp("Type", tag)) - { - fontType = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("Status", tag)) - { - fontStatus = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("MediaCompletionType", tag)) - { - mediaCompletionType = string;; - } - - textBuffer.clear(); - accumulateText= false; - - if (responseDepth == 0) - { - // We finished all of the XML, process the data - processResponse(tag); - } - } -} - -// -------------------------------------------------------------------------------- - -void LLWebRTCProtocolParser::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) - textBuffer.append(buffer, length); -} - -// -------------------------------------------------------------------------------- - -LLDate LLWebRTCProtocolParser::expiryTimeStampToLLDate(const std::string& WebRTC_ts) -{ - // *HACK: WebRTC reports the time incorrectly. LLDate also only parses a - // subset of valid ISO 8601 dates (only handles Z, not offsets). - // So just use the date portion and fix the time here. - std::string time_stamp = WebRTC_ts.substr(0, 10); - time_stamp += VOICE_FONT_EXPIRY_TIME; - - LL_DEBUGS("WebRTCProtocolParser") << "WebRTC timestamp " << WebRTC_ts << " modified to: " << time_stamp << LL_ENDL; - - return LLDate(time_stamp); -} - -// -------------------------------------------------------------------------------- - -void LLWebRTCProtocolParser::processResponse(std::string tag) -{ - LL_DEBUGS("WebRTCProtocolParser") << 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(); - LL_DEBUGS("LowVoice") << eventTypeCstr << LL_ENDL; - - if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) - { - // These happen so often that logging them is pretty useless. - LL_DEBUGS("LowVoice") << "Updated Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << isModeratorMuted << ", " << isSpeaking << ", " << volume << ", " << energy << LL_ENDL; - LLWebRTCVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); - } - else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) - { - LLWebRTCVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); - } - else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-1408789@bhr.WebRTC.com - true - false - - - */ - LLWebRTCVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) - { - LLWebRTCVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent")) - { - //nothng useful to process for this event, but we should not WARN that we have received it. - } - else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) - { - LLWebRTCVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - 200 - OK - 2 - false - - */ - LLWebRTCVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); - } - else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) - { - /* - - - AuxBufferAudioCapture - - */ - LLWebRTCVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); - } - else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.WebRTC.com - xI5auBZ60SJWIk606-1JGRQ== - - 0 - - */ - LL_DEBUGS("LowVoice") << "Added Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << ", " << displayNameString << ", " << participantType << LL_ENDL; - LLWebRTCVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); - } - else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.WebRTC.com - xtx7YNV-3SGiG7rA1fo5Ndw== - - */ - LL_DEBUGS("LowVoice") << "Removed params:" << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << LL_ENDL; - - LLWebRTCVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); - } - else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) - { - // These are really spammy in tuning mode - LLWebRTCVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); - } - else if (!stricmp(eventTypeCstr, "MessageEvent")) - { - //TODO: This probably is not received any more, it was used to support SLim clients - LLWebRTCVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) - { - //TODO: This probably is not received any more, it was used to support SLim clients - LLWebRTCVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); - } - else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-9@bhd.WebRTC.com - 0 - 50 - 1 - 0 - 000 - 0 - - */ - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) - { - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) - { - LLWebRTCVoiceClient::getInstance()->voiceServiceConnectionStateChangedEvent(statusCode, statusString, mBuildID); - } - else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent")) - { - /* - - RenderDeviceChanged< / EventType> - - Speakers(Turtle Beach P11 Headset)< / Device> - Speakers(Turtle Beach P11 Headset)< / DisplayName> - SpecificDevice< / Type> - < / RelevantDevice> - < / Event> - */ - // an audio device was removed or added, fetch and update the local list of audio devices. - } - else - { - LL_WARNS("WebRTCProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; - } - } - else - { - const char *actionCstr = actionString.c_str(); - LL_DEBUGS("LowVoice") << actionCstr << LL_ENDL; - if (!stricmp(actionCstr, "Session.Set3DPosition.1")) - { - // We don't need to process these - } - else if (!stricmp(actionCstr, "Connector.Create.1")) - { - LLWebRTCVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); - } - else if (!stricmp(actionCstr, "Account.Login.1")) - { - LLWebRTCVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); - } - else if (!stricmp(actionCstr, "Session.Create.1")) - { - LLWebRTCVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) - { - LLWebRTCVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "Session.Connect.1")) - { - LLWebRTCVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.Logout.1")) - { - LLWebRTCVoiceClient::getInstance()->logoutResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) - { - LLWebRTCVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) - { - LLWebRTCVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) - { - LLWebRTCVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Aux.SetVadProperties.1")) - { - // both values of statusCode (old and more recent) indicate valid requests - if (statusCode != 0 && statusCode != 200) - { - LL_WARNS("Voice") << "Aux.SetVadProperties.1 request failed: " - << "statusCode: " << statusCode - << " and " - << "statusString: " << statusString - << LL_ENDL; - } - } - /* - else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) - { - LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString); - } - 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")) - { - - } - */ - } -} LLWebRTCSecurity::LLWebRTCSecurity() { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f4e2ac0b7f..e040552ab2 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -712,8 +712,6 @@ private: // We should kill the voice daemon in case of connection alert bool mTerminateDaemon; - friend class LLWebRTCProtocolParser; - std::string mAccountName; std::string mAccountPassword; std::string mChannelSDP; @@ -956,118 +954,6 @@ private: LLEventMailDrop mWebRTCPump; }; - -/** - * @class LLWebRTCProtocolParser - * @brief This class helps construct new LLIOPipe specializations - * @see LLIOPipe - * - * THOROUGH_DESCRIPTION - */ -class LLWebRTCProtocolParser : public LLIOPipe -{ - LOG_CLASS(LLWebRTCProtocolParser); -public: - LLWebRTCProtocolParser(); - virtual ~LLWebRTCProtocolParser(); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - 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; - std::string statusString; - std::string requestId; - std::string actionString; - std::string connectorHandle; - std::string versionID; - std::string mBuildID; - std::string accountHandle; - std::string sessionHandle; - 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; - std::string uriString; - bool isChannel; - bool incoming; - bool enabled; - std::string nameString; - std::string audioMediaString; - std::string deviceString; - std::string displayNameString; - int participantType; - bool isLocallyMuted; - bool isModeratorMuted; - bool isSpeaking; - int volume; - F32 energy; - std::string messageHeader; - std::string messageBody; - std::string notificationType; - bool hasText; - bool hasAudio; - bool hasVideo; - bool terminated; - std::string blockMask; - std::string presenceOnly; - std::string autoAcceptMask; - std::string autoAddAsBuddy; - int numberOfAliases; - std::string subscriptionHandle; - std::string subscriptionType; - S32 id; - std::string descriptionString; - LLDate expirationDate; - bool hasExpired; - 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); - LLDate expiryTimeStampToLLDate(const std::string& WebRTC_ts); - -}; - class LLWebRTCSecurity : public LLSingleton { LLSINGLETON(LLWebRTCSecurity); -- cgit v1.2.3 From c43df8fbd75f8eeff69745a3187e2d7e0838cd95 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 21:20:23 -0700 Subject: disable unused but set warnings on newer compilers on mac. --- indra/cmake/00-Common.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 897eabb233..26a4162e42 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -182,7 +182,9 @@ if (LINUX OR DARWIN) list(APPEND GCC_WARNINGS -Wno-reorder -Wno-non-virtual-dtor ) -# list(APPEND GCC_WARNINGS -Wno-unused-but-set-variable -Wno-unused-variable ) + 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}) -- cgit v1.2.3 From 0a844ef04703270bd20a1871206eb5b868d5f67b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 21:45:42 -0700 Subject: Fix connection failed logic to do a renegotiate. Also, remove some dead code. --- indra/llwebrtc/llwebrtc.cpp | 10 +++---- indra/newview/llvoicewebrtc.cpp | 64 ----------------------------------------- 2 files changed, 4 insertions(+), 70 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 93e9db9c1d..862325c3f1 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -425,15 +425,13 @@ void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConne } break; } - case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: + case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: { - if (new_state == webrtc::PeerConnectionInterface::PeerConnectionState::kConnected) + for (auto &observer : mSignalingObserverList) { - for (auto &observer : mSignalingObserverList) - { - observer->OnRenegotiationNeeded(); - } + observer->OnRenegotiationNeeded(); } + break; } default: diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 23033c5fee..bbe54018e8 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2633,70 +2633,6 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) void LLWebRTCVoiceClient::sendLocalAudioUpdates() { - // Check all of the dirty states and then send messages to those needing to be changed. - // Tuningmode hands its own mute settings. - std::ostringstream stream; - - if (mMuteMicDirty && !mTuningMode) - { - mMuteMicDirty = false; - - // Send a local mute command. - - LL_INFOS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << (mMuteMic ? "true" : "false") << "" - << "\n\n\n"; - - } - - if (mSpeakerMuteDirty && !mTuningMode) - { - const char *muteval = ((mSpeakerVolume <= 0.0) ? "true" : "false"); - - mSpeakerMuteDirty = false; - - LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << muteval << "" - << "\n\n\n"; - - } - - if (mSpeakerVolumeDirty) - { - mSpeakerVolumeDirty = false; - - LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << mSpeakerVolume << "" - << "\n\n\n"; - - } - - if (mMicVolumeDirty) - { - mMicVolumeDirty = false; - - LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << mMicVolume << "" - << "\n\n\n"; - } - - - if (!stream.str().empty()) - { - writeString(stream.str()); - } } /** -- cgit v1.2.3 From b476e1545bb398bcda7a9dd693ac3fcc5ab4b7b8 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 22:49:39 -0700 Subject: fix some retry logic and speaker volume logic --- indra/llwebrtc/llwebrtc.cpp | 4 +++- indra/newview/llvoicewebrtc.cpp | 12 +++++++++--- indra/newview/llvoicewebrtc.h | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 862325c3f1..93981e4076 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -37,6 +37,8 @@ namespace llwebrtc { +const float VOLUME_SCALE_WEBRTC = 3.0f; + void LLWebRTCImpl::init() { mAnswerReceived = false; @@ -356,7 +358,7 @@ void LLWebRTCImpl::setSpeakerVolume(float volume) { webrtc::AudioTrackInterface* audio_track = static_cast(track); webrtc::AudioSourceInterface* source = audio_track->GetSource(); - source->SetVolume(10.0 * volume); + source->SetVolume(VOLUME_SCALE_WEBRTC * volume); } } diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index bbe54018e8..d66435e2bc 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -833,7 +833,6 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; setLoginInfo(voiceUserName, voicePassword, channelSDP); - setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -1518,6 +1517,10 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) { return false; } + if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) + { + break; + } if (mSessionTerminateRequested) { @@ -2825,17 +2828,20 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * { LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; + float speaker_volume = 0; audio_interface->setMute(true); { LLMutexLock lock(&mVoiceStateMutex); - - audio_interface->setSpeakerVolume(mSpeakerVolume); + speaker_volume = mSpeakerVolume; } + audio_interface->setSpeakerVolume(mSpeakerVolume); + setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } void LLWebRTCVoiceClient::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; + mRelogRequested = TRUE; setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e040552ab2..0dad2909d4 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -829,7 +829,7 @@ private: bool mSpeakerVolumeDirty; bool mSpeakerMuteDirty; - int mSpeakerVolume; + float mSpeakerVolume; int mMicVolume; bool mMicVolumeDirty; -- cgit v1.2.3 From c01d83b5302afb48c8cad9954a58f294c2bd0a78 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 22:50:06 -0700 Subject: build fix on newer osx machines --- indra/newview/llviewerhelp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index 3181ae6283..61e70dc820 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, LLMediaCtrl* web) override { LLViewerHelp* vhelp = LLViewerHelp::getInstance(); if (! vhelp) -- cgit v1.2.3 From 162c38f1d56246eb382a80b6a99703a839082b2c Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 20:48:33 -0700 Subject: Remove bad session from janus when negotation fails and is retried. --- indra/llwebrtc/llwebrtc.cpp | 18 +- indra/newview/llvoicewebrtc.cpp | 361 +++++++++++++++------------------------- indra/newview/llvoicewebrtc.h | 2 - 3 files changed, 143 insertions(+), 238 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 93981e4076..edf941436e 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -246,15 +246,15 @@ bool LLWebRTCImpl::initializeConnection() config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer 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.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); webrtc::PeerConnectionDependencies pc_dependencies(this); auto error_or_peer_connection = mPeerConnectionFactory->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d66435e2bc..bd4eff8bcf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -595,7 +595,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() U32 retry = 0; - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT, VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); do { @@ -611,90 +611,89 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() switch (getVoiceControlState()) { - case VOICE_STATE_TP_WAIT: - // starting point for voice - if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) - { - LL_DEBUGS("Voice") << "Suspending voiceControlCoro() momentarily for teleport. Tuning: " << mTuningMode << ". Relog: " << mRelogRequested << LL_ENDL; - llcoro::suspendUntilTimeout(1.0); - } - else - { - setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); - } - break; + case VOICE_STATE_TP_WAIT: + // starting point for voice + if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) + { + LL_DEBUGS("Voice") << "Suspending voiceControlCoro() momentarily for teleport. Tuning: " << mTuningMode + << ". Relog: " << mRelogRequested << LL_ENDL; + llcoro::suspendUntilTimeout(1.0); + } + else + { + setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); + } + break; - case VOICE_STATE_START_SESSION: - if (establishVoiceConnection() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) - { - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_SESSION_START, VOICE_STATE_SESSION_RETRY); - } - else - { - setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); - } - break; + case VOICE_STATE_START_SESSION: + if (establishVoiceConnection() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) + { + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_SESSION_START, VOICE_STATE_SESSION_RETRY); + } + else + { + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); + } + break; - case VOICE_STATE_WAIT_FOR_SESSION_START: - { - llcoro::suspendUntilTimeout(1.0); - std::string channel_sdp; - { - LLMutexLock lock(&mVoiceStateMutex); - if (mVoiceControlState == VOICE_STATE_SESSION_RETRY) - { - break; - } + case VOICE_STATE_WAIT_FOR_SESSION_START: + { + llcoro::suspendUntilTimeout(1.0); + std::string channel_sdp; + { + LLMutexLock lock(&mVoiceStateMutex); + if (mVoiceControlState == VOICE_STATE_SESSION_RETRY) + { + break; + } if (!mChannelSDP.empty()) { mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; } - } - break; - } - case VOICE_STATE_PROVISION_ACCOUNT: - if (!provisionVoiceAccount()) - { - setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); + } + break; } - else - { - setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); - } - break; - case VOICE_STATE_SESSION_PROVISION_WAIT: - llcoro::suspendUntilTimeout(1.0); - break; - + case VOICE_STATE_PROVISION_ACCOUNT: + if (!provisionVoiceAccount()) + { + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); + } + else + { + setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); + } + break; + case VOICE_STATE_SESSION_PROVISION_WAIT: + llcoro::suspendUntilTimeout(1.0); + break; - case VOICE_STATE_SESSION_RETRY: - giveUp(); // cleans sockets and session - if (mRelogRequested) - { - // We failed to connect, give it a bit time before retrying. - retry++; - F32 full_delay = llmin(5.f * (F32)retry, 60.f); - F32 current_delay = 0.f; - LL_INFOS("Voice") << "Voice failed to establish session after " << retry - << " tries. Will attempt to reconnect in " << full_delay - << " seconds" << LL_ENDL; - while (current_delay < full_delay && !sShuttingDown) + case VOICE_STATE_SESSION_RETRY: + giveUp(); // cleans sockets and session + if (mRelogRequested) { - // Assuming that a second has passed is not accurate, - // but we don't need accurancy here, just to make sure - // that some time passed and not to outlive voice itself - current_delay++; - llcoro::suspendUntilTimeout(1.f); + // We failed to connect, give it a bit time before retrying. + retry++; + F32 full_delay = llmin(5.f * (F32) retry, 60.f); + F32 current_delay = 0.f; + LL_INFOS("Voice") << "Voice failed to establish session after " << retry << " tries. Will attempt to reconnect in " + << full_delay << " seconds" << LL_ENDL; + while (current_delay < full_delay && !sShuttingDown) + { + // Assuming that a second has passed is not accurate, + // but we don't need accurancy here, just to make sure + // that some time passed and not to outlive voice itself + current_delay++; + llcoro::suspendUntilTimeout(1.f); + } + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); } - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); - } - else - { - setVoiceControlStateUnless(VOICE_STATE_DONE); - } - break; + else + { + setVoiceControlStateUnless(VOICE_STATE_DONE); + } + break; - case VOICE_STATE_SESSION_ESTABLISHED: + case VOICE_STATE_SESSION_ESTABLISHED: { if (mTuningMode) { @@ -705,39 +704,52 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } break; - case VOICE_STATE_WAIT_FOR_CHANNEL: + case VOICE_STATE_WAIT_FOR_CHANNEL: { - if (!waitForChannel()) // todo: split into more states like login/fonts + if ((!waitForChannel()) || !mVoiceEnabled) // todo: split into more states like login/fonts { setVoiceControlStateUnless(VOICE_STATE_DISCONNECT, VOICE_STATE_SESSION_RETRY); } - // on true, it's a retry, so let the state stand. + // on true, it's a retry, so let the state stand. } break; - case VOICE_STATE_DISCONNECT: - LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; - endAndDisconnectSession(); - retry = 0; // Connected without issues - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); - break; + case VOICE_STATE_DISCONNECT: + LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; + breakVoiceConnection(true); + retry = 0; // Connected without issues + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); + break; - case VOICE_STATE_WAIT_FOR_EXIT: - if (mRelogRequested && mVoiceEnabled) - { - LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); - } - else + case VOICE_STATE_WAIT_FOR_EXIT: + if (mRelogRequested && mVoiceEnabled) + { + LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); + } + else + { + setVoiceControlStateUnless(VOICE_STATE_DONE); + } + break; + + case VOICE_STATE_DONE: + if (mVoiceEnabled) + { + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); + } + else + { + llcoro::suspendUntilTimeout(1.0); + } + break; + default: { - setVoiceControlStateUnless(VOICE_STATE_DONE); + LL_WARNS("Voice") << "Unknown voice control state " << getVoiceControlState() << LL_ENDL; + break; } - break; - - case VOICE_STATE_DONE: - break; } - } while (getVoiceControlState() > 0); + } while (true); if (sShuttingDown) { @@ -749,15 +761,6 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() LL_INFOS("Voice") << "exiting" << LL_ENDL; } -bool LLWebRTCVoiceClient::endAndDisconnectSession() -{ - LL_DEBUGS("Voice") << LL_ENDL; - - breakVoiceConnection(true); - - return true; -} - bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) { if (!sShuttingDown && mVoiceEnabled) @@ -883,57 +886,40 @@ bool LLWebRTCVoiceClient::establishVoiceConnection() bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) { - LL_DEBUGS("Voice") << "( wait=" << corowait << ")" << LL_ENDL; - bool retval(true); - mShutdownComplete = false; - connectorShutdown(); + LL_INFOS("Voice") << "Breaking voice account." << LL_ENDL; - if (corowait) + while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) { - LLSD timeoutResult(LLSDMap("connector", "timeout")); - - LLSD result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - - retval = result.has("connector"); + LL_DEBUGS("Voice") << "no capabilities for voice breaking; waiting " << LL_ENDL; + // *TODO* Pump a message for wake up. + llcoro::suspend(); } - else + + if (sShuttingDown) { - mRelogRequested = false; //stop the control coro - // If we are not doing a corowait then we must sleep until the connector has responded - // otherwise we may very well close the socket too early. -#if LL_WINDOWS - if (!mShutdownComplete) - { - // The situation that brings us here is a call from ::terminate() - // At this point message system is already down so we can't wait for - // the message, yet we need to receive "connector shutdown response". - // Either wait a bit and emulate it or check gMessageSystem for specific message - _sleep(1000); - if (sConnected) - { - sConnected = false; - LLSD WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); - mWebRTCPump.post(WebRTCevent); - } - mShutdownComplete = true; - } -#endif + return false; } - LL_DEBUGS("Voice") << "closing SLVoice socket" << LL_ENDL; - closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. - cleanUp(); - sConnected = false; + std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); + + LL_DEBUGS("Voice") << "region ready for voice break; url=" << url << LL_ENDL; - return retval; + LL_DEBUGS("Voice") << "sending ProvisionVoiceAccountRequest (breaking) (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << 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; + httpAdapter->postAndSuspend(httpRequest, url, body); + return true; } bool LLWebRTCVoiceClient::loginToWebRTC() { - - mRelogRequested = false; mIsLoggedIn = true; notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); @@ -956,41 +942,9 @@ void LLWebRTCVoiceClient::logoutOfWebRTC(bool wait) { if (mIsLoggedIn) { - // Ensure that we'll re-request provisioning before logging in again mAccountPassword.clear(); - - logoutSendMessage(); - - if (wait) - { - LLSD timeoutResult(LLSDMap("logout", "timeout")); - LLSD result; - - do - { - LL_DEBUGS("Voice") - << "waiting for logout response on " - << mWebRTCPump.getName() - << LL_ENDL; - - result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - - if (sShuttingDown) - { - break; - } - - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - // Don't get confused by prior queued events -- note that it's - // very important that mWebRTCPump is an LLEventMailDrop, which - // does queue events. - } while (! result["logout"]); - } - else - { - LL_DEBUGS("Voice") << "not waiting for logout" << LL_ENDL; - } - + breakVoiceConnection(wait); + // Ensure that we'll re-request provisioning before logging in again mIsLoggedIn = false; } } @@ -1314,10 +1268,7 @@ typedef enum e_voice_wait_for_channel_state VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, VOICE_CHANNEL_STATE_PROCESS_CHANNEL, VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, - VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK, - VOICE_CHANNEL_STATE_LOGOUT, - VOICE_CHANNEL_STATE_RELOG, - VOICE_CHANNEL_STATE_DONE, + VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK } EVoiceWaitForChannelState; bool LLWebRTCVoiceClient::waitForChannel() @@ -1422,52 +1373,8 @@ bool LLWebRTCVoiceClient::waitForChannel() << " RelogRequested=" << mRelogRequested << " VoiceEnabled=" << mVoiceEnabled << LL_ENDL; - state = VOICE_CHANNEL_STATE_LOGOUT; - break; + return !sShuttingDown; } - - case VOICE_CHANNEL_STATE_LOGOUT: - logoutOfWebRTC(true /*bool wait*/); - if (mRelogRequested) - { - state = VOICE_CHANNEL_STATE_RELOG; - } - else - { - state = VOICE_CHANNEL_STATE_DONE; - } - break; - - case VOICE_CHANNEL_STATE_RELOG: - LL_DEBUGS("Voice") << "Relog Requested, restarting provisioning" << LL_ENDL; - if (!provisionVoiceAccount()) - { - if (sShuttingDown) - { - return false; - } - LL_WARNS("Voice") << "provisioning voice failed; giving up" << LL_ENDL; - giveUp(); - return false; - } - if (mVoiceEnabled && mRelogRequested) - { - state = VOICE_CHANNEL_STATE_LOGIN; - } - else - { - state = VOICE_CHANNEL_STATE_DONE; - } - break; - case VOICE_CHANNEL_STATE_DONE: - LL_DEBUGS("Voice") - << "exiting" - << " RelogRequested=" << mRelogRequested - << " VoiceEnabled=" << mVoiceEnabled - << LL_ENDL; - return !sShuttingDown; - case VOICE_CHANNEL_STATE_CHECK_EFFECTS: - break; } } while (true); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 0dad2909d4..eb3893fe2b 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -669,8 +669,6 @@ private: return mVoiceControlState; } - bool endAndDisconnectSession(); - bool callbackEndDaemon(const LLSD& data); bool provisionVoiceAccount(); void OnVoiceAccountProvisioned(const LLSD& body); -- cgit v1.2.3 From e0126bfe904ff2d26021a8fa227a75898562778a Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 21:38:40 -0700 Subject: fix some shutdown logic in webrtc code --- indra/newview/llvoicewebrtc.cpp | 49 +++++------------------------------------ 1 file changed, 6 insertions(+), 43 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index bd4eff8bcf..92f107bb7c 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -369,23 +369,10 @@ void LLWebRTCVoiceClient::terminate() { return; } - - // needs to be done manually here since we will not get another pass in - // coroutines... that mechanism is long since gone. - if (mIsLoggedIn) - { - logoutOfWebRTC(false); - } - if(sConnected) - { - breakVoiceConnection(false); - sConnected = false; - } - else - { - mRelogRequested = false; - } + mRelogRequested = false; + mVoiceEnabled = false; + llwebrtc::init(); sShuttingDown = true; sPump = NULL; @@ -540,8 +527,7 @@ void LLWebRTCVoiceClient::idle(void* user_data) typedef enum e_voice_control_coro_state { VOICE_STATE_ERROR = -1, - VOICE_STATE_DONE = 0, - VOICE_STATE_TP_WAIT, // entry point + VOICE_STATE_TP_WAIT = 0, // entry point VOICE_STATE_START_DAEMON, VOICE_STATE_PROVISION_ACCOUNT, VOICE_STATE_SESSION_PROVISION_WAIT, @@ -685,12 +671,8 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() current_delay++; llcoro::suspendUntilTimeout(1.f); } - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); - } - else - { - setVoiceControlStateUnless(VOICE_STATE_DONE); } + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); break; case VOICE_STATE_SESSION_ESTABLISHED: @@ -729,20 +711,10 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { - setVoiceControlStateUnless(VOICE_STATE_DONE); + llcoro::suspendUntilTimeout(1.0); } break; - case VOICE_STATE_DONE: - if (mVoiceEnabled) - { - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); - } - else - { - llcoro::suspendUntilTimeout(1.0); - } - break; default: { LL_WARNS("Voice") << "Unknown voice control state " << getVoiceControlState() << LL_ENDL; @@ -750,15 +722,6 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } } } while (true); - - if (sShuttingDown) - { - // LLWebRTCVoiceClient might be already dead - return; - } - - mIsCoroutineActive = false; - LL_INFOS("Voice") << "exiting" << LL_ENDL; } bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) -- cgit v1.2.3 From af2842793d2e5ce169cb5bced280bc227de01baa Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 22:48:55 -0700 Subject: go ahead and restart voice negotiation when voice is re-enabled --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 92f107bb7c..93fdf8be34 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -704,7 +704,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() break; case VOICE_STATE_WAIT_FOR_EXIT: - if (mRelogRequested && mVoiceEnabled) + if (mVoiceEnabled) { LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); -- cgit v1.2.3 From b1906593003b82fb8b442540e4ec69101f2c17c3 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 23:13:47 -0700 Subject: fix newer xcode build warning --- indra/newview/llvoicewebrtc.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 93fdf8be34..66bed5f0b5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1227,7 +1227,6 @@ bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) typedef enum e_voice_wait_for_channel_state { VOICE_CHANNEL_STATE_LOGIN = 0, // entry point - VOICE_CHANNEL_STATE_CHECK_EFFECTS, VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, VOICE_CHANNEL_STATE_PROCESS_CHANNEL, VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, -- cgit v1.2.3 From 639e63faab239b88d41c8e2c755509e9dcdc6251 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 18 Sep 2023 11:25:47 -0700 Subject: Fix voice device settings --- indra/llwebrtc/llwebrtc.cpp | 94 +++++++++++++++++++++------- indra/llwebrtc/llwebrtc_impl.h | 3 + indra/newview/llpanelvoicedevicesettings.cpp | 79 ++++++++++++----------- 3 files changed, 120 insertions(+), 56 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index edf941436e..96be2c1f0b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -55,7 +55,7 @@ void LLWebRTCImpl::init() mSignalingThread->SetName("WebRTCSignalingThread", nullptr); mSignalingThread->Start(); - mSignalingThread->PostTask( + mWorkerThread->PostTask( [this]() { mDeviceModule = webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, @@ -68,7 +68,7 @@ void LLWebRTCImpl::init() void LLWebRTCImpl::refreshDevices() { - mSignalingThread->PostTask([this]() { updateDevices(); }); + mWorkerThread->PostTask([this]() { updateDevices(); }); } void LLWebRTCImpl::setDevicesObserver(LLWebRTCDevicesObserver *observer) { mVoiceDevicesObserverList.emplace_back(observer); } @@ -85,41 +85,48 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer) void LLWebRTCImpl::setCaptureDevice(const std::string &id) { - mSignalingThread->PostTask( + mWorkerThread->PostTask( [this, id]() { + mDeviceModule->StopRecording(); int16_t captureDeviceCount = mDeviceModule->RecordingDevices(); for (int16_t index = 0; index < captureDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mDeviceModule->RecordingDeviceName(index, name, guid); - if (id == guid || id == name) + if (id == guid || id == "Default") { + RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << index; mDeviceModule->SetRecordingDevice(index); break; } } + mDeviceModule->InitRecording(); + mDeviceModule->StartRecording(); }); } void LLWebRTCImpl::setRenderDevice(const std::string &id) { - mSignalingThread->PostTask( + mWorkerThread->PostTask( [this, id]() { + mDeviceModule->StopPlayout(); int16_t renderDeviceCount = mDeviceModule->RecordingDevices(); for (int16_t index = 0; index < renderDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mDeviceModule->PlayoutDeviceName(index, name, guid); - if (id == guid || id == name) + if (id == guid || id == "Default") { mDeviceModule->SetPlayoutDevice(index); break; } } + mDeviceModule->InitPlayout(); + mDeviceModule->StartPlayout(); }); } @@ -156,7 +163,7 @@ void LLWebRTCImpl::updateDevices() void LLWebRTCImpl::setTuningMode(bool enable) { - mSignalingThread->PostTask( + mWorkerThread->PostTask( [this, enable]() { if (enable) @@ -181,7 +188,7 @@ void LLWebRTCImpl::OnCaptureData(const void *audio_samples, const size_t num_channels, const uint32_t samples_per_sec) { - if (bytes_per_sample != 4) + if (bytes_per_sample != 2) { return; } @@ -220,28 +227,31 @@ void LLWebRTCImpl::unsetSignalingObserver(LLWebRTCSignalingObserver *observer) } } + bool LLWebRTCImpl::initializeConnection() { RTC_DCHECK(!mPeerConnection); - RTC_DCHECK(!mPeerConnectionFactory); + RTC_DCHECK(mPeerConnectionFactory); mAnswerReceived = false; + + mSignalingThread->PostTask([this]() { initializeConnectionThreaded(); }); + return true; +} + + + +bool LLWebRTCImpl::initializeConnectionThreaded() +{ mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(), mWorkerThread.get(), mSignalingThread.get(), - nullptr /* default_adm */, + mDeviceModule, webrtc::CreateBuiltinAudioEncoderFactory(), webrtc::CreateBuiltinAudioDecoderFactory(), nullptr /* video_encoder_factory */, nullptr /* video_decoder_factory */, nullptr /* audio_mixer */, nullptr /* audio_processing */); - - if (!mPeerConnectionFactory) - { - shutdownConnection(); - return false; - } - webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer server; @@ -272,16 +282,34 @@ bool LLWebRTCImpl::initializeConnection() cricket::AudioOptions audioOptions; audioOptions.auto_gain_control = true; - audioOptions.echo_cancellation = true; + audioOptions.echo_cancellation = false; // incompatible with opus stereo audioOptions.noise_suppression = true; rtc::scoped_refptr stream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); rtc::scoped_refptr audio_track( - mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(cricket::AudioOptions()).get())); + mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(audioOptions).get())); audio_track->set_enabled(true); stream->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 = 1; + codecparam.parameters["stereo"] = "0"; + codecparam.parameters["sprop-stereo"] = "0"; + + params.codecs.push_back(codecparam); + sender->SetParameters(params); + } + mPeerConnection->SetLocalDescription(rtc::scoped_refptr(this)); RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); @@ -297,6 +325,12 @@ void LLWebRTCImpl::shutdownConnection() void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) { + std::istringstream sdp_stream(sdp); + std::string sdp_line; + while (std::getline(sdp_stream, sdp_line)) + { + RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp_line; + } mSignalingThread->PostTask( [this, sdp]() { @@ -515,10 +549,28 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) auto desc = mPeerConnection->pending_local_description(); std::string sdp; desc->ToString(&sdp); - RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << 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; + while (std::getline(sdp_stream, sdp_line)) { + int bandwidth = 0; + int payload_id = 0; + RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_line; + // force mono + if (std::sscanf(sdp_line.c_str(), "a=rtpmap:%i opus/%i/2", &payload_id, &bandwidth) == 2) + { + sdp_mangled_stream << sdp_line << "\n"; + } + else + { + sdp_mangled_stream << sdp_line << "\n"; + } + } for (auto &observer : mSignalingObserverList) { - observer->OnOfferAvailable(sdp); + observer->OnOfferAvailable(sdp_mangled_stream.str()); } } diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 5c6cfcdbc6..3f9b06cae7 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -160,6 +160,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void OnSetLocalDescriptionComplete(webrtc::RTCError error) override; protected: + + bool initializeConnectionThreaded(); + std::unique_ptr mNetworkThread; std::unique_ptr mWorkerThread; std::unique_ptr mSignalingThread; diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 28631e2b7b..48c90d6856 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -236,44 +236,47 @@ 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++) - { - 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; - } + LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices(); + if (devices.size() > 0) // if zero, we've not received our devices yet + { + 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; + } + } } 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++) - { - 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; - } + LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices(); + if (devices.size() > 0) // if zero, we've not received our devices yet + { + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); + + 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() -- cgit v1.2.3 From 8859312b1f0d975793c6c2a3d7b23b9880c657c5 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Sep 2023 10:14:29 -0700 Subject: add datachannel support --- indra/llwebrtc/llwebrtc.cpp | 50 ++++++++++++++++++++++++++++++++++++++++- indra/llwebrtc/llwebrtc.h | 16 +++++++++++++ indra/llwebrtc/llwebrtc_impl.h | 20 ++++++++++++++++- indra/newview/llvoicewebrtc.cpp | 9 ++++++++ indra/newview/llvoicewebrtc.h | 13 +++++++++-- 5 files changed, 104 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 96be2c1f0b..ac5870eab3 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -314,7 +314,22 @@ bool LLWebRTCImpl::initializeConnectionThreaded() RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); - return mPeerConnection != nullptr; + webrtc::DataChannelInit init; + init.ordered = true; + init.reliable = true; + auto data_channel_or_error = mPeerConnection->CreateDataChannelOrError("SLData", &init); + if (data_channel_or_error.ok()) + { + mDataChannel = std::move(data_channel_or_error.value()); + } + else + { + shutdownConnection(); + return false; + } + mDataChannel->RegisterObserver(this); + + return true; } void LLWebRTCImpl::shutdownConnection() @@ -574,9 +589,42 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) } } +// +// DataChannelObserver implementation +// +void LLWebRTCImpl::OnMessage(const webrtc::DataBuffer& buffer) +{ + std::string data((const char*)buffer.data.cdata(), buffer.size()); + for (auto &observer : mDataObserverList) + { + observer->OnDataReceived(data, buffer.binary); + } +} + +void LLWebRTCImpl::sendData(const std::string& data, bool binary) +{ + rtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length()); + webrtc::DataBuffer buffer(cowBuffer, binary); + mDataChannel->Send(buffer); +} + +void LLWebRTCImpl::setDataObserver(LLWebRTCDataObserver* observer) { mDataObserverList.emplace_back(observer); } + +void LLWebRTCImpl::unsetDataObserver(LLWebRTCDataObserver* observer) +{ + std::vector::iterator it = + std::find(mDataObserverList.begin(), mDataObserverList.end(), observer); + if (it != mDataObserverList.end()) + { + mDataObserverList.erase(it); + } +} + rtc::RefCountedObject *gWebRTCImpl = nullptr; LLWebRTCDeviceInterface *getDeviceInterface() { return gWebRTCImpl; } LLWebRTCSignalInterface *getSignalingInterface() { return gWebRTCImpl; } +LLWebRTCDataInterface *getDataInterface() { return gWebRTCImpl; } + void init() { diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index ca558add01..a6e754684e 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -96,6 +96,21 @@ class LLWebRTCAudioInterface virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 }; +class LLWebRTCDataObserver +{ +public: + virtual void OnDataReceived(const std::string& data, bool binary) = 0; +}; + +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; +}; + class LLWebRTCSignalingObserver { public: @@ -124,6 +139,7 @@ class LLWebRTCSignalInterface LLSYMEXPORT LLWebRTCDeviceInterface* getDeviceInterface(); LLSYMEXPORT LLWebRTCSignalInterface* getSignalingInterface(); +LLSYMEXPORT LLWebRTCDataInterface* getDataInterface(); } #endif // LLWEBRTC_H diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 3f9b06cae7..1ad117c7f3 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -66,11 +66,13 @@ namespace llwebrtc class LLWebRTCImpl : public LLWebRTCDeviceInterface, public LLWebRTCSignalInterface, public LLWebRTCAudioInterface, + public LLWebRTCDataInterface, public webrtc::AudioDeviceDataObserver, public webrtc::PeerConnectionObserver, public webrtc::CreateSessionDescriptionObserver, public webrtc::SetRemoteDescriptionObserverInterface, - public webrtc::SetLocalDescriptionObserverInterface + public webrtc::SetLocalDescriptionObserverInterface, + public webrtc::DataChannelObserver { public: @@ -126,6 +128,13 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // void setMute(bool mute) override; void setSpeakerVolume(float folume) override; // range 0.0-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. @@ -158,6 +167,12 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // SetLocalDescriptionObserverInterface implementation. // void OnSetLocalDescriptionComplete(webrtc::RTCError error) override; + + // + // DataChannelObserver implementation. + // + void OnStateChange() override {} + void OnMessage(const webrtc::DataBuffer& buffer) override; protected: @@ -184,6 +199,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, bool mAnswerReceived; rtc::scoped_refptr mPeerConnection; + + std::vector mDataObserverList; + rtc::scoped_refptr mDataChannel; }; } diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 66bed5f0b5..9013de67f5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -361,6 +361,9 @@ void LLWebRTCVoiceClient::init(LLPumpIO *pump) mWebRTCSignalingInterface = llwebrtc::getSignalingInterface(); mWebRTCSignalingInterface->setSignalingObserver(this); + + mWebRTCDataInterface = llwebrtc::getDataInterface(); + mWebRTCDataInterface->setDataObserver(this); } void LLWebRTCVoiceClient::terminate() @@ -2707,6 +2710,12 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } +void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) +{ + +} + + void LLWebRTCVoiceClient::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index eb3893fe2b..0d6988e1ef 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -59,7 +59,8 @@ class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface, public llwebrtc::LLWebRTCDevicesObserver, - public llwebrtc::LLWebRTCSignalingObserver + public llwebrtc::LLWebRTCSignalingObserver, + public llwebrtc::LLWebRTCDataObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); @@ -254,6 +255,13 @@ public: void OnOfferAvailable(const std::string &sdp) override; void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + //@} + + ///////////////////////// + /// @name Data Notification + /// LLWebRTCDataObserver + //@{ + void OnDataReceived(const std::string& data, bool binary) override; //@} void processIceUpdates(); @@ -761,7 +769,8 @@ private: llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; llwebrtc::LLWebRTCSignalInterface *mWebRTCSignalingInterface; - llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; LLVoiceDeviceList mCaptureDevices; LLVoiceDeviceList mRenderDevices; -- cgit v1.2.3 From 1cd8f6f4f88f7717f0fcafbb5d47de0af59d5fb7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 21 Sep 2023 15:28:58 -0700 Subject: Stream audio levels to and from viewers via DataChannels --- indra/llwebrtc/llwebrtc.cpp | 204 ++++++++++++++---- indra/llwebrtc/llwebrtc.h | 13 +- indra/llwebrtc/llwebrtc_impl.h | 10 +- indra/newview/llvoicewebrtc.cpp | 444 +++++++++++----------------------------- indra/newview/llvoicewebrtc.h | 24 ++- 5 files changed, 316 insertions(+), 379 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index ac5870eab3..77b050cbd0 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -62,10 +62,39 @@ void LLWebRTCImpl::init() mTaskQueueFactory.get(), std::unique_ptr(this)); mDeviceModule->Init(); + mDeviceModule->SetStereoRecording(false); + mDeviceModule->EnableBuiltInAEC(false); updateDevices(); }); } +void LLWebRTCImpl::terminate() +{ + mSignalingThread->BlockingCall( + [this]() + { + if (mPeerConnection) + { + mPeerConnection->Close(); + mPeerConnection = nullptr; + } + }); + mWorkerThread->BlockingCall( + [this]() + { + if (mDeviceModule) + { + mDeviceModule = nullptr; + } + }); + + mNetworkThread->Stop(); + mWorkerThread->Stop(); + mSignalingThread->Stop(); + +} + + void LLWebRTCImpl::refreshDevices() { mWorkerThread->PostTask([this]() { updateDevices(); }); @@ -88,22 +117,33 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id) mWorkerThread->PostTask( [this, id]() { - mDeviceModule->StopRecording(); + bool was_recording = mDeviceModule->Recording(); + + if (was_recording) + { + mDeviceModule->StopRecording(); + } int16_t captureDeviceCount = mDeviceModule->RecordingDevices(); - for (int16_t index = 0; index < captureDeviceCount; index++) + int16_t index = 0; /* default to first one if no match */ + for (int16_t i = 0; i < captureDeviceCount; i++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; - mDeviceModule->RecordingDeviceName(index, name, guid); + mDeviceModule->RecordingDeviceName(i, name, guid); if (id == guid || id == "Default") { - RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << index; - mDeviceModule->SetRecordingDevice(index); + RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; + index = i; break; } } + mDeviceModule->SetRecordingDevice(index); + mDeviceModule->InitMicrophone(); mDeviceModule->InitRecording(); - mDeviceModule->StartRecording(); + if (was_recording) + { + mDeviceModule->StartRecording(); + } }); } @@ -112,21 +152,32 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) mWorkerThread->PostTask( [this, id]() { - mDeviceModule->StopPlayout(); - int16_t renderDeviceCount = mDeviceModule->RecordingDevices(); - for (int16_t index = 0; index < renderDeviceCount; index++) + bool was_playing = mDeviceModule->Playing(); + if (was_playing) + { + mDeviceModule->StopPlayout(); + } + int16_t renderDeviceCount = mDeviceModule->PlayoutDevices(); + int16_t index = 0; /* default to first one if no match */ + for (int16_t i = 0; i < renderDeviceCount; i++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; - mDeviceModule->PlayoutDeviceName(index, name, guid); + mDeviceModule->PlayoutDeviceName(i, name, guid); if (id == guid || id == "Default") { - mDeviceModule->SetPlayoutDevice(index); + RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; + index = i; break; } } + mDeviceModule->SetPlayoutDevice(index); + mDeviceModule->InitSpeaker(); mDeviceModule->InitPlayout(); - mDeviceModule->StartPlayout(); + if (was_playing) + { + mDeviceModule->StartPlayout(); + } }); } @@ -141,10 +192,6 @@ void LLWebRTCImpl::updateDevices() mDeviceModule->PlayoutDeviceName(index, name, guid); renderDeviceList.emplace_back(name, guid); } - for (auto &observer : mVoiceDevicesObserverList) - { - observer->OnRenderDevicesChanged(renderDeviceList); - } int16_t captureDeviceCount = mDeviceModule->RecordingDevices(); LLWebRTCVoiceDeviceList captureDeviceList; @@ -157,7 +204,7 @@ void LLWebRTCImpl::updateDevices() } for (auto &observer : mVoiceDevicesObserverList) { - observer->OnCaptureDevicesChanged(captureDeviceList); + observer->OnDevicesChanged(renderDeviceList, captureDeviceList); } } @@ -188,11 +235,6 @@ void LLWebRTCImpl::OnCaptureData(const void *audio_samples, const size_t num_channels, const uint32_t samples_per_sec) { - if (bytes_per_sample != 2) - { - return; - } - double energy = 0; const short *samples = (const short *) audio_samples; for (size_t index = 0; index < num_samples * num_channels; index++) @@ -242,6 +284,21 @@ bool LLWebRTCImpl::initializeConnection() bool LLWebRTCImpl::initializeConnectionThreaded() { + rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); + webrtc::AudioProcessing::Config apm_config; + 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; + 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->ApplyConfig(apm_config); + mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(), mWorkerThread.get(), mSignalingThread.get(), @@ -251,7 +308,7 @@ bool LLWebRTCImpl::initializeConnectionThreaded() nullptr /* video_encoder_factory */, nullptr /* video_decoder_factory */, nullptr /* audio_mixer */, - nullptr /* audio_processing */); + apm); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer server; @@ -278,6 +335,17 @@ bool LLWebRTCImpl::initializeConnectionThreaded() return false; } + 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); + } + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); cricket::AudioOptions audioOptions; @@ -305,7 +373,6 @@ bool LLWebRTCImpl::initializeConnectionThreaded() codecparam.num_channels = 1; codecparam.parameters["stereo"] = "0"; codecparam.parameters["sprop-stereo"] = "0"; - params.codecs.push_back(codecparam); sender->SetParameters(params); } @@ -313,21 +380,6 @@ bool LLWebRTCImpl::initializeConnectionThreaded() mPeerConnection->SetLocalDescription(rtc::scoped_refptr(this)); RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); - - webrtc::DataChannelInit init; - init.ordered = true; - init.reliable = true; - auto data_channel_or_error = mPeerConnection->CreateDataChannelOrError("SLData", &init); - if (data_channel_or_error.ok()) - { - mDataChannel = std::move(data_channel_or_error.value()); - } - else - { - shutdownConnection(); - return false; - } - mDataChannel->RegisterObserver(this); return true; } @@ -414,6 +466,18 @@ void LLWebRTCImpl::setSpeakerVolume(float volume) }); } +void LLWebRTCImpl::requestAudioLevel() +{ + mWorkerThread->PostTask( + [this]() + { + for (auto &observer : mAudioObserverList) + { + observer->OnAudioLevel((float)mTuningEnergy); + } + }); +} + // // PeerConnectionObserver implementation. // @@ -429,6 +493,13 @@ void LLWebRTCImpl::OnRemoveTrack(rtc::scoped_refptrid(); } +void LLWebRTCImpl::OnDataChannel(rtc::scoped_refptr channel) +{ + mDataChannel = channel; + channel->RegisterObserver(this); +} + + void LLWebRTCImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) { LLWebRTCSignalingObserver::IceGatheringState webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; @@ -469,10 +540,14 @@ void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConne { if (new_state == webrtc::PeerConnectionInterface::PeerConnectionState::kConnected) { - for (auto &observer : mSignalingObserverList) - { - observer->OnAudioEstablished(this); - } + mWorkerThread->PostTask([this]() { + mDeviceModule->StartRecording(); + mDeviceModule->StartPlayout(); + for (auto &observer : mSignalingObserverList) + { + observer->OnAudioEstablished(this); + } + }); } break; } @@ -589,9 +664,44 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) } } +void LLWebRTCImpl::setAudioObserver(LLWebRTCAudioObserver *observer) { mAudioObserverList.emplace_back(observer); } + +void LLWebRTCImpl::unsetAudioObserver(LLWebRTCAudioObserver *observer) +{ + std::vector::iterator it = std::find(mAudioObserverList.begin(), mAudioObserverList.end(), observer); + if (it != mAudioObserverList.end()) + { + mAudioObserverList.erase(it); + } +} + // // DataChannelObserver implementation // + +void LLWebRTCImpl::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"; + 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 LLWebRTCImpl::OnMessage(const webrtc::DataBuffer& buffer) { std::string data((const char*)buffer.data.cdata(), buffer.size()); @@ -632,4 +742,12 @@ void init() gWebRTCImpl->AddRef(); gWebRTCImpl->init(); } + +void terminate() +{ + gWebRTCImpl->terminate(); + gWebRTCImpl->Release(); + gWebRTCImpl = nullptr; +} + } // namespace llwebrtc diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index a6e754684e..f1ba1620e3 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -45,6 +45,7 @@ namespace llwebrtc { LLSYMEXPORT void init(); +LLSYMEXPORT void terminate(); struct LLWebRTCIceCandidate { @@ -69,8 +70,7 @@ typedef std::vector LLWebRTCVoiceDeviceList; class LLWebRTCDevicesObserver { public: - virtual void OnRenderDevicesChanged(const LLWebRTCVoiceDeviceList &render_devices) = 0; - virtual void OnCaptureDevicesChanged(const LLWebRTCVoiceDeviceList &capture_devices) = 0; + virtual void OnDevicesChanged(const LLWebRTCVoiceDeviceList &render_devices, const LLWebRTCVoiceDeviceList &capture_devices) = 0; }; class LLWebRTCDeviceInterface @@ -89,11 +89,20 @@ class LLWebRTCDeviceInterface virtual double getTuningMicrophoneEnergy() = 0; }; +class LLWebRTCAudioObserver +{ + public: + virtual void OnAudioLevel(float level) = 0; +}; + class LLWebRTCAudioInterface { public: + virtual void setAudioObserver(LLWebRTCAudioObserver *observer) = 0; + virtual void unsetAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void setMute(bool mute) = 0; virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 + virtual void requestAudioLevel() = 0; }; class LLWebRTCDataObserver diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 1ad117c7f3..1670d10705 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -83,6 +83,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, ~LLWebRTCImpl() {} void init(); + void terminate(); // // LLWebRTCDeviceInterface @@ -126,8 +127,11 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // // LLWebRTCAudioInterface // + void setAudioObserver(LLWebRTCAudioObserver *observer) override; + void unsetAudioObserver(LLWebRTCAudioObserver *observer) override; void setMute(bool mute) override; void setSpeakerVolume(float folume) override; // range 0.0-1.0 + void requestAudioLevel() override; // // LLWebRTCDataInterface @@ -144,7 +148,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void OnAddTrack(rtc::scoped_refptr receiver, const std::vector> &streams) override; void OnRemoveTrack(rtc::scoped_refptr receiver) override; - void OnDataChannel(rtc::scoped_refptr channel) override {} + void OnDataChannel(rtc::scoped_refptr channel) override; void OnRenegotiationNeeded() override {} void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {}; void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override; @@ -171,7 +175,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // // DataChannelObserver implementation. // - void OnStateChange() override {} + void OnStateChange() override; void OnMessage(const webrtc::DataBuffer& buffer) override; protected: @@ -200,6 +204,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, rtc::scoped_refptr mPeerConnection; + std::vector mAudioObserverList; + std::vector mDataObserverList; rtc::scoped_refptr mDataChannel; }; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9013de67f5..da27ff7320 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -74,6 +74,9 @@ // for base64 decoding #include "apr_base64.h" +#include "json/reader.h" +#include "json/writer.h" + #define USE_SESSION_GROUPS 0 #define VX_NULL_POSITION -2147483648.0 /*The Silence*/ @@ -88,7 +91,7 @@ namespace { static const std::string VOICE_SERVER_TYPE = "WebRTC"; // Don't send positional updates more frequently than this: - const F32 UPDATE_THROTTLE_SECONDS = 0.5f; + const F32 UPDATE_THROTTLE_SECONDS = 0.1f; // Timeout for connection to WebRTC const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; @@ -375,7 +378,7 @@ void LLWebRTCVoiceClient::terminate() mRelogRequested = false; mVoiceEnabled = false; - llwebrtc::init(); + llwebrtc::terminate(); sShuttingDown = true; sPump = NULL; @@ -684,7 +687,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() { performMicTuning(); } - + sessionEstablished(); setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_CHANNEL, VOICE_STATE_SESSION_RETRY); } break; @@ -1116,6 +1119,9 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession // Just flush it all out and start new. mWebRTCPump.discard(); + // add 'self' participant. + addParticipantByID(gAgent.getID()); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); return true; @@ -1922,16 +1928,6 @@ void LLWebRTCVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePt } -void LLWebRTCVoiceClient::OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList& render_devices) -{ - clearCaptureDevices(); - for (auto &device : render_devices) - { - LLWebRTCVoiceClient::addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); - } - LLWebRTCVoiceClient::setDevicesListUpdated(true); -} - void LLWebRTCVoiceClient::clearCaptureDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; @@ -1967,13 +1963,19 @@ void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) mDevicesListUpdated = state; } -void LLWebRTCVoiceClient::OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) +void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) { clearRenderDevices(); for (auto &device : render_devices) { addRenderDevice(LLVoiceDevice(device.display_name, device.id)); } + clearCaptureDevices(); + for (auto &device : capture_devices) + { + addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); + } setDevicesListUpdated(true); } @@ -2105,7 +2107,7 @@ bool LLWebRTCVoiceClient::deviceSettingsAvailable() { bool result = true; - if(mRenderDevices.empty()) + if(mRenderDevices.empty() || mCaptureDevices.empty()) result = false; return result; @@ -2113,11 +2115,7 @@ bool LLWebRTCVoiceClient::deviceSettingsAvailable() bool LLWebRTCVoiceClient::deviceSettingsUpdated() { bool updated = mDevicesListUpdated; - if (mDevicesListUpdated) - { - // 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 - } + mDevicesListUpdated = false; return updated; } @@ -2285,20 +2283,12 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) void LLWebRTCVoiceClient::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 << "" - << "" << getAudioSessionHandle() << ""; - - stream << ""; LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); @@ -2321,38 +2311,6 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) pos.mdV[i] = VX_NULL_POSITION; } } - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << "" - ; - - stream << ""; - - stream << ""; LLVector3d earPosition; LLVector3 earVelocity; @@ -2397,40 +2355,13 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) pos.mdV[i] = VX_NULL_POSITION; } } - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << "" - ; - - stream << ""; + } - stream << "1"; //do not generate responses for update requests - stream << "\n\n\n"; - } + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->requestAudioLevel(); + } + if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty)) { @@ -2469,41 +2400,12 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) } 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 << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << volume << "" - << "\n\n\n"; - - if(!mAudioSession->mIsP2P) - { - // Send a "mute for me" command for the user - // Doesn't work in P2P sessions - stream << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << (mute?"1":"0") << "" - << "Audio" - << "\n\n\n"; - } } p->mVolumeDirty = false; } } } - - std::string update(stream.str()); - if(!update.empty()) - { - LL_DEBUGS("VoiceUpdate") << "sending update " << update << LL_ENDL; - writeString(update); - } - } void LLWebRTCVoiceClient::sendLocalAudioUpdates() @@ -2700,6 +2602,7 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * { LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; + mWebRTCAudioInterface->setAudioObserver(this); float speaker_volume = 0; audio_interface->setMute(true); { @@ -2710,9 +2613,68 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } +void LLWebRTCVoiceClient::OnAudioLevel(float level) +{ + if (mWebRTCDataInterface) + { + Json::FastWriter writer; + Json::Value root; + root["p"] = (UINT32) (level * 256); + std::string json_data = writer.write(root); + + mWebRTCDataInterface->sendData(json_data, false); + } +} + void LLWebRTCVoiceClient::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' - join - object of join data (TBD) (true for now) + // 'l' - boolean, always true if exists. + + 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; + } + for (auto &participant_id : voice_data.getMemberNames()) + { + std::string foo = participant_id; + LL_WARNS("Voice") << "Participant ID (" << participant_id << "):" << data << LL_ENDL; + + LLUUID agent_id(participant_id); + if (agent_id.isNull()) + { + LL_WARNS("Voice") << "Bad participant ID from data channel (" << participant_id << "):" << data << LL_ENDL; + continue; + } + participantStatePtr_t participant = findParticipantByID(agent_id); + if (participant) + { + participant->mPower = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we + can use it at some point when we actually process frames. */ + participant->mIsSpeaking = participant->mPower > 0.05; + } + } + } } @@ -2994,7 +2956,7 @@ void LLWebRTCVoiceClient::sessionAddedEvent( session->mAlternateSIPURI = session->mSIPURI; // and generate a proper URI from the ID. - setSessionURI(session, sipURIFromID(session->mCallerID)); + setSessionURI(session, session->mCallerID.asString()); } else { @@ -3065,7 +3027,7 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) if(!session->mIsChannel) { // this is a p2p session. Make sure the other end is added as a participant. - participantStatePtr_t participant(session->addParticipant(session->mSIPURI)); + participantStatePtr_t participant(session->addParticipant(LLUUID(session->mSIPURI))); if(participant) { if(participant->mAvatarIDValid) @@ -3365,7 +3327,7 @@ void LLWebRTCVoiceClient::participantAddedEvent( sessionStatePtr_t session(findSession(sessionHandle)); if(session) { - participantStatePtr_t participant(session->addParticipant(uriString)); + participantStatePtr_t participant(session->addParticipant(LLUUID(uriString))); if(participant) { participant->mAccountName = nameString; @@ -3424,92 +3386,6 @@ void LLWebRTCVoiceClient::participantRemovedEvent( } } - -void LLWebRTCVoiceClient::participantUpdatedEvent( - 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; - - participant->mIsSpeaking = isSpeaking; - participant->mIsModeratorMuted = isModeratorMuted; - - // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false - if (isSpeaking) - { - participant->mSpeakingTimeout.reset(); - participant->mPower = energy; - } - else - { - participant->mPower = 0.0f; - } - - // Ignore incoming volume level if it has been explicitly set, or there - // is a volume or mute change pending. - if ( !participant->mVolumeSet && !participant->mVolumeDirty) - { - participant->mVolume = (F32)volume * VOLUME_SCALE_WEBRTC; - } - - // *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 - if (voice_cnl && voice_cnl->getSessionID().notNull()) - { - LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); - if (speaker_manager) - { - speaker_manager->update(true); - - // also initialize voice moderate_mode depend on Agent's participant. See EXT-6937. - // *TODO: remove once a way to request the current voice channel moderation mode is implemented. - if (gAgent.getID() == participant->mAvatarID) - { - speaker_manager->initVoiceModerateMode(); - } - } - } - } - else - { - LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - void LLWebRTCVoiceClient::messageEvent( std::string &sessionHandle, std::string &uriString, @@ -3743,8 +3619,9 @@ void LLWebRTCVoiceClient::muteListChanged() ///////////////////////////// // Managing list of participants -LLWebRTCVoiceClient::participantState::participantState(const std::string &uri) : - mURI(uri), +LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : + mURI(agent_id.asString()), + mAvatarID(agent_id), mPTT(false), mIsSpeaking(false), mIsModeratorMuted(false), @@ -3760,55 +3637,27 @@ LLWebRTCVoiceClient::participantState::participantState(const std::string &uri) { } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const std::string &uri) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) { participantStatePtr_t result; - bool useAlternateURI = false; - // Note: this is mostly the body of LLWebRTCVoiceClient::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. - { - participantMap::iterator iter = mParticipantsByURI.find(uri); + participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); - if(iter == mParticipantsByURI.end()) - { - if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) - { - // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. - // Use mSIPURI instead, since it will be properly encoded. - iter = mParticipantsByURI.find(mSIPURI); - useAlternateURI = true; - } - } - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } + if (iter != mParticipantsByUUID.end()) + { + 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)); + result.reset(new participantState(agent_id)); + mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); mParticipantsChanged = true; - - // Try to do a reverse transform on the URI to get the GUID back. - { - LLUUID id; - if(LLWebRTCVoiceClient::getInstance()->IDFromName(result->mURI, id)) - { - result->mAvatarIDValid = true; - result->mAvatarID = id; - } - else - { - // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This indicates that the ID will not be in the name cache. - result->mAvatarID.generate(uri); - } - } + + result->mAvatarIDValid = true; + result->mAvatarID = agent_id; if(result->updateMuteState()) { @@ -3983,6 +3832,16 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantB return result; } +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const LLUUID &id) +{ + participantStatePtr_t result; + if (mAudioSession) + { + result = mAudioSession->addParticipant(id); + } + return result; +} + // Check for parcel boundary crossing @@ -4156,68 +4015,14 @@ bool LLWebRTCVoiceClient::setSpatialChannel( void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) { - std::string userURI = sipURIFromID(uuid); - - switchChannel(userURI, false, true, true); + switchChannel(uuid.asString(), false, true, true); } -#if 0 -// WebRTC text IMs are not in use. -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::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); - - 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 LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) { -#if 0 - // WebRTC 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 + } bool LLWebRTCVoiceClient::isValidChannel(std::string &sessionHandle) { @@ -4361,16 +4166,6 @@ bool LLWebRTCVoiceClient::inProximalChannel() return result; } -std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID &id) -{ - std::string result; - result = "sip:"; - result += nameFromID(id); - result += "@"; - - return result; -} - std::string LLWebRTCVoiceClient::nameFromAvatar(LLVOAvatar *avatar) { std::string result; @@ -4785,10 +4580,6 @@ BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) participantStatePtr_t participant(findParticipantByID(id)); if(participant) { - if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) - { - participant->mIsSpeaking = FALSE; - } result = participant->mIsSpeaking; } @@ -4814,7 +4605,8 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID& id) participantStatePtr_t participant(findParticipantByID(id)); if(participant) { - result = participant->mPower; + LL_WARNS("Voice") << "Power:" << participant->mPower << LL_ENDL; + result = participant->mPower*4; } return result; @@ -5178,7 +4970,10 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceCli } } - +void LLWebRTCVoiceClient::sessionEstablished() +{ + addSession(gAgent.getRegion()->getRegionID().asString()); +} LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const std::string &handle) { @@ -6367,6 +6162,7 @@ void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() } } +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } LLWebRTCSecurity::LLWebRTCSecurity() diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 0d6988e1ef..eb898ab4eb 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -60,6 +60,7 @@ class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceEffectInterface, public llwebrtc::LLWebRTCDevicesObserver, public llwebrtc::LLWebRTCSignalingObserver, + public llwebrtc::LLWebRTCAudioObserver, public llwebrtc::LLWebRTCDataObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); @@ -80,6 +81,8 @@ public: // 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 //@{ @@ -212,7 +215,6 @@ public: void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - std::string sipURIFromID(const LLUUID &id) override; //@} /// @name LLVoiceEffectInterface virtual implementations @@ -242,8 +244,8 @@ public: /// @name Devices change notification // LLWebRTCDevicesObserver //@{ - void OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; - void OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; + void OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override; //@} ////////////////////////////// @@ -256,6 +258,13 @@ public: void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; //@} + + ////////////////////////////// + /// @name Signaling notification + // LLWebRTCAudioObserver + //@{ + void OnAudioLevel(float level) override; + //@} ///////////////////////// /// @name Data Notification @@ -307,7 +316,7 @@ protected: struct participantState { public: - participantState(const std::string &uri); + participantState(const LLUUID& agent_id); bool updateMuteState(); // true if mute state has changed bool isAvatar(); @@ -348,7 +357,7 @@ protected: static ptr_t createSession(); ~sessionState(); - participantStatePtr_t addParticipant(const std::string &uri); + participantStatePtr_t addParticipant(const LLUUID& agent_id); void removeParticipant(const participantStatePtr_t &participant); void removeAllParticipants(); @@ -496,7 +505,6 @@ protected: void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); - void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); void voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id); void auxAudioPropertiesEvent(F32 energy); void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); @@ -545,7 +553,7 @@ protected: void filePlaybackSetMode(bool vox = false, float speed = 1.0f); participantStatePtr_t findParticipantByID(const LLUUID& id); - + participantStatePtr_t addParticipantByID(const LLUUID &id); #if 0 //////////////////////////////////////// @@ -556,7 +564,7 @@ protected: sessionIterator sessionsBegin(void); sessionIterator sessionsEnd(void); #endif - + void sessionEstablished(); sessionStatePtr_t findSession(const std::string &handle); sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri); sessionStatePtr_t findSession(const LLUUID &participant_id); -- cgit v1.2.3 From dc56d0104d1c54fd2b9c6925cefbeac04eadcca8 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 21 Sep 2023 22:33:57 -0700 Subject: deal with add/remove of participants more effectively. --- indra/llwebrtc/llwebrtc.cpp | 5 ----- indra/newview/llvoicewebrtc.cpp | 25 +++++++++++++++++++++---- indra/newview/llvoicewebrtc.h | 3 ++- 3 files changed, 23 insertions(+), 10 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 77b050cbd0..1913786c0b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -87,11 +87,6 @@ void LLWebRTCImpl::terminate() mDeviceModule = nullptr; } }); - - mNetworkThread->Stop(); - mWorkerThread->Stop(); - mSignalingThread->Stop(); - } diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index da27ff7320..a942eb8288 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2619,7 +2619,7 @@ void LLWebRTCVoiceClient::OnAudioLevel(float level) { Json::FastWriter writer; Json::Value root; - root["p"] = (UINT32) (level * 256); + root["p"] = (uint32_t) (level * 256); std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); @@ -2656,9 +2656,6 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) } for (auto &participant_id : voice_data.getMemberNames()) { - std::string foo = participant_id; - LL_WARNS("Voice") << "Participant ID (" << participant_id << "):" << data << LL_ENDL; - LLUUID agent_id(participant_id); if (agent_id.isNull()) { @@ -2666,8 +2663,16 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) continue; } participantStatePtr_t participant = findParticipantByID(agent_id); + if (!participant && voice_data[participant_id].get("j", Json::Value(false)).asBool()) + { + participant = addParticipantByID(agent_id); + } if (participant) { + if(voice_data[participant_id].get("l", Json::Value(false)).asBool()) + { + removeParticipantByID(agent_id); + } participant->mPower = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we can use it at some point when we actually process frames. */ @@ -3842,6 +3847,18 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantBy return result; } +void LLWebRTCVoiceClient::removeParticipantByID(const LLUUID &id) +{ + participantStatePtr_t result; + if (mAudioSession) + { + participantStatePtr_t participant = mAudioSession->findParticipantByID(id); + if (participant) + { + mAudioSession->removeParticipant(participant); + } + } +} // Check for parcel boundary crossing diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index eb898ab4eb..6ddda0ef94 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -554,7 +554,8 @@ protected: participantStatePtr_t findParticipantByID(const LLUUID& id); participantStatePtr_t addParticipantByID(const LLUUID &id); - + void removeParticipantByID(const LLUUID &id); + #if 0 //////////////////////////////////////// // voice sessions. -- cgit v1.2.3 From 4624184f97a94cd99df3640f5fc1e596c30bbffe Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 21 Sep 2023 23:23:49 -0700 Subject: send a message to the server when we're ready for data channel data --- indra/llwebrtc/llwebrtc.cpp | 17 +++++++++++++++-- indra/llwebrtc/llwebrtc.h | 1 + indra/newview/llvoicewebrtc.cpp | 10 ++++++++++ indra/newview/llvoicewebrtc.h | 1 + 4 files changed, 27 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 1913786c0b..dc746b7629 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -78,13 +78,22 @@ void LLWebRTCImpl::terminate() mPeerConnection->Close(); mPeerConnection = nullptr; } + mPeerConnectionFactory = nullptr; }); mWorkerThread->BlockingCall( [this]() { - if (mDeviceModule) + mDeviceModule = nullptr; + mTaskQueueFactory = nullptr; + + }); + mNetworkThread->BlockingCall( + [this]() + { + if (mDataChannel) { - mDeviceModule = nullptr; + mDataChannel->Close(); + mDataChannel = nullptr; } }); } @@ -681,6 +690,10 @@ void LLWebRTCImpl::OnStateChange() { case webrtc::DataChannelInterface::kOpen: RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State Open"; + for (auto &observer : mDataObserverList) + { + observer->OnDataChannelReady(); + } break; case webrtc::DataChannelInterface::kConnecting: RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State Connecting"; diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index f1ba1620e3..d0d2834e3c 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -109,6 +109,7 @@ class LLWebRTCDataObserver { public: virtual void OnDataReceived(const std::string& data, bool binary) = 0; + virtual void OnDataChannelReady() = 0; }; class LLWebRTCDataInterface diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a942eb8288..b621f5ee92 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2682,6 +2682,16 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) } } +void LLWebRTCVoiceClient::OnDataChannelReady() +{ + // send a join + Json::FastWriter writer; + Json::Value root; + root["j"] = true; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); +} + void LLWebRTCVoiceClient::OnRenegotiationNeeded() { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 6ddda0ef94..bc858dcb32 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -271,6 +271,7 @@ public: /// LLWebRTCDataObserver //@{ void OnDataReceived(const std::string& data, bool binary) override; + void OnDataChannelReady() override; //@} void processIceUpdates(); -- cgit v1.2.3 From c087b8648dc85cffdc4d9a5eb85d989a9b4aa51b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 10:47:48 -0700 Subject: Fix shutdown crash issue. --- indra/llwebrtc/llwebrtc.cpp | 72 ++++++++++++++++++++--------------------- indra/llwebrtc/llwebrtc.h | 6 ++-- indra/llwebrtc/llwebrtc_impl.h | 42 +++++++++++++----------- indra/newview/llvoicewebrtc.cpp | 24 +++++--------- indra/newview/llvoicewebrtc.h | 1 - 5 files changed, 70 insertions(+), 75 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index dc746b7629..a3dadd696c 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -39,6 +39,34 @@ namespace llwebrtc const float VOLUME_SCALE_WEBRTC = 3.0f; + +double LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } + +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) +{ + double energy = 0; + const short *samples = (const short *) audio_samples; + for (size_t index = 0; index < num_samples * num_channels; index++) + { + double sample = (static_cast(samples[index]) / (double) 32768); + energy += sample * sample; + } + mMicrophoneEnergy = std::sqrt(energy); +} + +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) +{ +} + + void LLWebRTCImpl::init() { mAnswerReceived = false; @@ -58,9 +86,10 @@ void LLWebRTCImpl::init() mWorkerThread->PostTask( [this]() { + mAudioDeviceObserver = new LLAudioDeviceObserver; mDeviceModule = webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, mTaskQueueFactory.get(), - std::unique_ptr(this)); + std::unique_ptr(mAudioDeviceObserver)); mDeviceModule->Init(); mDeviceModule->SetStereoRecording(false); mDeviceModule->EnableBuiltInAEC(false); @@ -83,6 +112,10 @@ void LLWebRTCImpl::terminate() mWorkerThread->BlockingCall( [this]() { + if (mDeviceModule) + { + mDeviceModule->Terminate(); + } mDeviceModule = nullptr; mTaskQueueFactory = nullptr; @@ -231,32 +264,6 @@ void LLWebRTCImpl::setTuningMode(bool enable) }); } -double LLWebRTCImpl::getTuningMicrophoneEnergy() { return mTuningEnergy; } - -void LLWebRTCImpl::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) -{ - double energy = 0; - const short *samples = (const short *) audio_samples; - for (size_t index = 0; index < num_samples * num_channels; index++) - { - double sample = (static_cast(samples[index]) / (double) 32768); - energy += sample * sample; - } - mTuningEnergy = std::sqrt(energy); -} - -void LLWebRTCImpl::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) -{ -} - // // LLWebRTCSignalInterface // @@ -470,16 +477,9 @@ void LLWebRTCImpl::setSpeakerVolume(float volume) }); } -void LLWebRTCImpl::requestAudioLevel() +double LLWebRTCImpl::getAudioLevel() { - mWorkerThread->PostTask( - [this]() - { - for (auto &observer : mAudioObserverList) - { - observer->OnAudioLevel((float)mTuningEnergy); - } - }); + return mAudioDeviceObserver->getMicrophoneEnergy(); } // diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index d0d2834e3c..30c0d63c55 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -86,13 +86,12 @@ class LLWebRTCDeviceInterface virtual void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; virtual void setTuningMode(bool enable) = 0; - virtual double getTuningMicrophoneEnergy() = 0; + virtual double getAudioLevel() = 0; }; class LLWebRTCAudioObserver { public: - virtual void OnAudioLevel(float level) = 0; }; class LLWebRTCAudioInterface @@ -101,8 +100,7 @@ class LLWebRTCAudioInterface virtual void setAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void unsetAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void setMute(bool mute) = 0; - virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 - virtual void requestAudioLevel() = 0; + virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 = 0; }; class LLWebRTCDataObserver diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 1670d10705..da829e52af 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -63,11 +63,31 @@ namespace llwebrtc { +class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver +{ + public: + double getMicrophoneEnergy(); + + 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; + + 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: + double mMicrophoneEnergy; +}; + class LLWebRTCImpl : public LLWebRTCDeviceInterface, public LLWebRTCSignalInterface, public LLWebRTCAudioInterface, public LLWebRTCDataInterface, - public webrtc::AudioDeviceDataObserver, public webrtc::PeerConnectionObserver, public webrtc::CreateSessionDescriptionObserver, public webrtc::SetRemoteDescriptionObserverInterface, @@ -77,7 +97,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, { public: LLWebRTCImpl() : - mTuningEnergy(0.0) + mAudioDeviceObserver(nullptr) { } ~LLWebRTCImpl() {} @@ -98,20 +118,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void setRenderDevice(const std::string& id) override; void setTuningMode(bool enable) override; - double getTuningMicrophoneEnergy() override; - - - 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; - - 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; + double getAudioLevel() override; // // LLWebRTCSignalInterface @@ -131,7 +138,6 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void unsetAudioObserver(LLWebRTCAudioObserver *observer) override; void setMute(bool mute) override; void setSpeakerVolume(float folume) override; // range 0.0-1.0 - void requestAudioLevel() override; // // LLWebRTCDataInterface @@ -195,7 +201,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, rtc::scoped_refptr mDeviceModule; std::vector mVoiceDevicesObserverList; - double mTuningEnergy; + LLAudioDeviceObserver * mAudioDeviceObserver; // signaling std::vector mSignalingObserverList; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b621f5ee92..e6877063b5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2100,7 +2100,7 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return mWebRTCDeviceInterface->getTuningMicrophoneEnergy(); + return mWebRTCDeviceInterface->getAudioLevel(); } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -2357,9 +2357,14 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) } } - if (mWebRTCAudioInterface) + if (mWebRTCDataInterface && mWebRTCAudioInterface) { - mWebRTCAudioInterface->requestAudioLevel(); + Json::FastWriter writer; + Json::Value root; + root["p"] = (uint32_t) ((F32)mWebRTCDeviceInterface->getAudioLevel() * 256); + std::string json_data = writer.write(root); + + mWebRTCDataInterface->sendData(json_data, false); } @@ -2613,19 +2618,6 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } -void LLWebRTCVoiceClient::OnAudioLevel(float level) -{ - if (mWebRTCDataInterface) - { - Json::FastWriter writer; - Json::Value root; - root["p"] = (uint32_t) (level * 256); - std::string json_data = writer.write(root); - - mWebRTCDataInterface->sendData(json_data, false); - } -} - void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) { // incoming data will be a json structure (if it's not binary.) We may pack diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index bc858dcb32..518fee53ef 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -263,7 +263,6 @@ public: /// @name Signaling notification // LLWebRTCAudioObserver //@{ - void OnAudioLevel(float level) override; //@} ///////////////////////// -- cgit v1.2.3 From fc5075dda58be36133b5795e056be7ff2f1e822b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 11:22:41 -0700 Subject: only send volume updates when volume has changed. --- indra/newview/llvoicewebrtc.cpp | 15 ++++++++++----- indra/newview/llvoicewebrtc.h | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index e6877063b5..3942f4171d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2359,12 +2359,17 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) if (mWebRTCDataInterface && mWebRTCAudioInterface) { - Json::FastWriter writer; - Json::Value root; - root["p"] = (uint32_t) ((F32)mWebRTCDeviceInterface->getAudioLevel() * 256); - std::string json_data = writer.write(root); + uint32_t audio_level = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); + if (audio_level != mAudioLevel) + { + Json::FastWriter writer; + Json::Value root; + root["p"] = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); + std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + mWebRTCDataInterface->sendData(json_data, false); + mAudioLevel = audio_level; + } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 518fee53ef..e3aa33bfd9 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -786,6 +786,8 @@ private: std::vector mIceCandidates; bool mIceCompleted; + uint32_t mAudioLevel; + bool mIsInitialized; bool mShutdownComplete; -- cgit v1.2.3 From baf01e50cfc1d59c5853aad3948f6e741995ee62 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 12:40:16 -0700 Subject: join notification was going out before session was created. --- indra/newview/llvoicewebrtc.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3942f4171d..3073a58183 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1121,6 +1121,13 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession // add 'self' participant. addParticipantByID(gAgent.getID()); + // tell peers that this participant has joined. + + Json::FastWriter writer; + Json::Value root; + root["j"] = true; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); @@ -2681,12 +2688,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) void LLWebRTCVoiceClient::OnDataChannelReady() { - // send a join - Json::FastWriter writer; - Json::Value root; - root["j"] = true; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + } -- cgit v1.2.3 From 0110e37c7af96b02fd4ad92144b1b042e307d6a5 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 16:55:13 -0700 Subject: Smooth voice power level reporting. --- indra/llwebrtc/llwebrtc.cpp | 21 +++++++++++++---- indra/llwebrtc/llwebrtc_impl.h | 5 +++- indra/newview/llvoicewebrtc.cpp | 52 +++++++++++++++++++++++++++++------------ 3 files changed, 58 insertions(+), 20 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index a3dadd696c..7b5ca7fb16 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -39,6 +39,7 @@ namespace llwebrtc const float VOLUME_SCALE_WEBRTC = 3.0f; +LLAudioDeviceObserver::LLAudioDeviceObserver() : mMicrophoneEnergy(0.0), mSumVector {0} {} double LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } @@ -48,14 +49,26 @@ void LLAudioDeviceObserver::OnCaptureData(const void *audio_samples, const size_t num_channels, const uint32_t samples_per_sec) { - double energy = 0; + float energy = 0; const short *samples = (const short *) audio_samples; for (size_t index = 0; index < num_samples * num_channels; index++) { - double sample = (static_cast(samples[index]) / (double) 32768); + float sample = (static_cast(samples[index]) / (float) 32768); energy += sample * sample; } - mMicrophoneEnergy = std::sqrt(energy); + + // 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, @@ -479,7 +492,7 @@ void LLWebRTCImpl::setSpeakerVolume(float volume) double LLWebRTCImpl::getAudioLevel() { - return mAudioDeviceObserver->getMicrophoneEnergy(); + return 20*mAudioDeviceObserver->getMicrophoneEnergy(); } // diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index da829e52af..70586f9013 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -66,6 +66,8 @@ namespace llwebrtc class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver { public: + LLAudioDeviceObserver(); + double getMicrophoneEnergy(); void OnCaptureData(const void *audio_samples, @@ -81,7 +83,8 @@ class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver const uint32_t samples_per_sec) override; protected: - double mMicrophoneEnergy; + float mSumVector[30]; // 300 ms of smoothing + float mMicrophoneEnergy; }; class LLWebRTCImpl : public LLWebRTCDeviceInterface, diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3073a58183..c22246d841 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -87,6 +87,7 @@ namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; const F32 SPEAKING_TIMEOUT = 1.f; + const F32 SPEAKING_AUDIO_LEVEL = 0.05; static const std::string VOICE_SERVER_TYPE = "WebRTC"; @@ -2366,16 +2367,28 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) if (mWebRTCDataInterface && mWebRTCAudioInterface) { - uint32_t audio_level = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); - if (audio_level != mAudioLevel) + F32 audio_level = 0.0; + + if (!mMuteMic) + { + audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); + } + uint32_t uint_audio_level = (uint32_t) (audio_level * 256); + if (uint_audio_level != mAudioLevel) { Json::FastWriter writer; - Json::Value root; - root["p"] = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); + Json::Value root; + root["p"] = uint_audio_level; std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); - mAudioLevel = audio_level; + mAudioLevel = uint_audio_level; + participantStatePtr_t participant = findParticipantByID(gAgentID); + if (participant) + { + participant->mPower = audio_level; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; + } } } @@ -2677,10 +2690,12 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) { removeParticipantByID(agent_id); } - participant->mPower = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + // convert to decibles + participant->mPower = energyRMS; /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we can use it at some point when we actually process frames. */ - participant->mIsSpeaking = participant->mPower > 0.05; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } } } @@ -4446,10 +4461,16 @@ void LLWebRTCVoiceClient::leaveChannel(void) void LLWebRTCVoiceClient::setMuteMic(bool muted) { + participantStatePtr_t participant = findParticipantByID(gAgentID); + if (participant) + { + participant->mPower = 0.0; + } if (mWebRTCAudioInterface) { mWebRTCAudioInterface->setMute(muted); } + mMuteMic = muted; } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -4625,16 +4646,17 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) return result; } -F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID& id) -{ - F32 result = 0; - participantStatePtr_t participant(findParticipantByID(id)); - if(participant) +F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) +{ + F32 result = 0; + if (!mMuteMic) { - LL_WARNS("Voice") << "Power:" << participant->mPower << LL_ENDL; - result = participant->mPower*4; + participantStatePtr_t participant(findParticipantByID(id)); + if (participant) + { + result = participant->mPower * 4; + } } - return result; } -- cgit v1.2.3 From d9ec32b1c3878dfa7f34b474d8278e13f2d7ece4 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 25 Sep 2023 20:03:18 -0700 Subject: send position and rotation data to voice server. --- indra/llmath/llquaternion.h | 1 + indra/newview/llvoicewebrtc.cpp | 248 +++++++--------------------------------- indra/newview/llvoicewebrtc.h | 4 +- 3 files changed, 46 insertions(+), 207 deletions(-) (limited to 'indra') diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h index 51ce163b4e..609d75a095 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/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index c22246d841..4c687b5807 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -746,7 +746,6 @@ bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) return false; } - bool LLWebRTCVoiceClient::provisionVoiceAccount() { LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; @@ -2153,127 +2152,6 @@ void LLWebRTCVoiceClient::giveUp() cleanUp(); } -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]; - F64 npos[3]; - - // The original XML command was sent like this: - /* - << "" - << "" << pos[VX] << "" - << "" << pos[VZ] << "" - << "" << pos[VY] << "" - << "" - << "" - << "" << mAvatarVelocity[VX] << "" - << "" << mAvatarVelocity[VZ] << "" - << "" << mAvatarVelocity[VY] << "" - << "" - << "" - << "" << l.mV[VX] << "" - << "" << u.mV[VX] << "" - << "" << a.mV[VX] << "" - << "" - << "" - << "" << l.mV[VZ] << "" - << "" << u.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VY] << "" - << "" << u.mV [VZ] << "" - << "" << a.mV [VY] << "" - << ""; - */ - -#if 1 - // This was the original transform done when building the XML command - nat[0] = left.mV[VX]; - nat[1] = up.mV[VX]; - nat[2] = at.mV[VX]; - - nup[0] = left.mV[VZ]; - nup[1] = up.mV[VY]; - nup[2] = at.mV[VZ]; - - nl[0] = left.mV[VY]; - nl[1] = up.mV[VZ]; - nl[2] = at.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY]; - -// nvel[0] = vel.mV[VX]; -// nvel[1] = vel.mV[VZ]; -// nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - - // This was the original transform done in the SDK - nat[0] = at.mV[2]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * left.mV[2]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = at.mV[0]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[0]; - - npos[2] = pos.mdV[2] * -1.0; - npos[1] = pos.mdV[1]; - npos[0] = pos.mdV[0]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } -#else - // This is the compose of the two transforms (at least, that's what I'm trying for) - nat[0] = at.mV[VX]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * up.mV[VZ]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = left.mV[VX]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY] * -1.0; - - nvel[0] = vel.mV[VX]; - nvel[1] = vel.mV[VZ]; - nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - -#endif -} - void LLWebRTCVoiceClient::setHidden(bool hidden) { mHidden = hidden; @@ -2291,82 +2169,39 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) { - if (mSpatialCoordsDirty && inSpatialChannel()) - { - LLVector3 l, u, a, vel; - LLVector3d pos; - - mSpatialCoordsDirty = false; - LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); -// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; - l = avatarRot.getLeftRow(); - u = avatarRot.getUpRow(); - a = avatarRot.getFwdRow(); - - pos = mAvatarPosition; - vel = mAvatarVelocity; - - // 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 (mWebRTCDataInterface && mWebRTCAudioInterface) + { + Json::Value root = Json::objectValue; - if (mHidden) + if (mSpatialCoordsDirty && inSpatialChannel()) { - for (int i=0;i<3;++i) - { - pos.mdV[i] = VX_NULL_POSITION; - } - } - - LLVector3d earPosition; - LLVector3 earVelocity; - LLMatrix3 earRot; - - switch(mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earVelocity = mCameraVelocity; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = avatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = mCameraRot; - break; - } + root["sp"] = Json::objectValue; + root["sp"]["x"] = (int)(mAvatarPosition[0]*100); + root["sp"]["y"] = (int)(mAvatarPosition[1]*100); + root["sp"]["z"] = (int)(mAvatarPosition[2]*100); + root["sh"] = Json::objectValue; + root["sh"]["x"] = (int)(mAvatarRot[0]*100); + root["sh"]["y"] = (int)(mAvatarRot[1]*100); + root["sh"]["z"] = (int)(mAvatarRot[2]*100); + root["sh"]["w"] = (int)(mAvatarRot[3]*100); + + + root["lp"] = Json::objectValue; + root["lp"]["x"] = (int)(mCameraPosition[0]*100); + root["lp"]["y"] = (int)(mCameraPosition[1]*100); + root["lp"]["z"] = (int)(mCameraPosition[2]*100); + root["lh"] = Json::objectValue; + root["lh"]["x"] = (int)(mCameraRot[0]*100); + root["lh"]["y"] = (int)(mCameraRot[1]*100); + root["lh"]["z"] = (int)(mCameraRot[2]*100); + root["lh"]["w"] = (int)(mCameraRot[3]*100); - l = earRot.getLeftRow(); - u = earRot.getUpRow(); - a = earRot.getFwdRow(); - - pos = earPosition; - vel = earVelocity; - - - oldSDKTransform(l, u, a, pos, vel); - - if (mHidden) - { - for (int i=0;i<3;++i) - { - pos.mdV[i] = VX_NULL_POSITION; - } + mSpatialCoordsDirty = false; } - } - - if (mWebRTCDataInterface && mWebRTCAudioInterface) - { + + F32 audio_level = 0.0; if (!mMuteMic) @@ -2376,19 +2211,24 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) uint32_t uint_audio_level = (uint32_t) (audio_level * 256); if (uint_audio_level != mAudioLevel) { - Json::FastWriter writer; - Json::Value root; root["p"] = uint_audio_level; - std::string json_data = writer.write(root); - - mWebRTCDataInterface->sendData(json_data, false); mAudioLevel = uint_audio_level; participantStatePtr_t participant = findParticipantByID(gAgentID); - if (participant) - { + if (participant) + { participant->mPower = audio_level; - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; - } + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; + } + } + + + if (root.size() > 0) + { + + Json::FastWriter writer; + std::string json_data = writer.write(root); + + mWebRTCDataInterface->sendData(json_data, false); } } @@ -4361,7 +4201,6 @@ void LLWebRTCVoiceClient::updatePosition(void) LLViewerRegion *region = gAgent.getRegion(); if(region && isAgentAvatarValid()) { - LLMatrix3 rot; LLVector3d pos; LLQuaternion qrot; @@ -4369,13 +4208,12 @@ void LLWebRTCVoiceClient::updatePosition(void) // 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()); pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); LLWebRTCVoiceClient::getInstance()->setCameraPosition( pos, // position LLVector3::zero, // velocity - rot); // rotation matrix + LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix // Send the current avatar position to the voice code qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); @@ -4392,7 +4230,7 @@ void LLWebRTCVoiceClient::updatePosition(void) } } -void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { mCameraRequestedPosition = position; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e3aa33bfd9..1d013c6609 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -521,7 +521,7 @@ protected: ///////////////////////////// // Sending updates of current state void updatePosition(void); - void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); bool channelFromRegion(LLViewerRegion *region, std::string &name); @@ -823,7 +823,7 @@ private: LLVector3d mCameraPosition; LLVector3d mCameraRequestedPosition; LLVector3 mCameraVelocity; - LLMatrix3 mCameraRot; + LLQuaternion mCameraRot; LLVector3d mAvatarPosition; LLVector3 mAvatarVelocity; -- cgit v1.2.3 From f6becec955bc673c7e0014eb89197fef9128a405 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 26 Sep 2023 16:37:24 -0700 Subject: sdd stereo support to client --- indra/llwebrtc/llwebrtc.cpp | 43 ++++++++++++++++++++++++++++++----------- indra/newview/llvoicewebrtc.cpp | 15 ++++++-------- 2 files changed, 38 insertions(+), 20 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 7b5ca7fb16..e3fd68d35b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -26,6 +26,7 @@ #include "llwebrtc_impl.h" #include +#include #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_encoder_factory.h" @@ -394,13 +395,28 @@ bool LLWebRTCImpl::initializeConnectionThreaded() codecparam.name = "opus"; codecparam.kind = cricket::MEDIA_TYPE_AUDIO; codecparam.clock_rate = 48000; - codecparam.num_channels = 1; - codecparam.parameters["stereo"] = "0"; - codecparam.parameters["sprop-stereo"] = "0"; + 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); + } + mPeerConnection->SetLocalDescription(rtc::scoped_refptr(this)); RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); @@ -416,12 +432,9 @@ void LLWebRTCImpl::shutdownConnection() void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) { - std::istringstream sdp_stream(sdp); - std::string sdp_line; - while (std::getline(sdp_stream, sdp_line)) - { - RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp_line; - } + + RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp; + mSignalingThread->PostTask( [this, sdp]() { @@ -661,20 +674,28 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) std::istringstream sdp_stream(sdp); std::ostringstream sdp_mangled_stream; std::string sdp_line; + int opus_payload = 0; while (std::getline(sdp_stream, sdp_line)) { int bandwidth = 0; int payload_id = 0; - RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_line; - // force mono + // force mono down, stereo up if (std::sscanf(sdp_line.c_str(), "a=rtpmap:%i opus/%i/2", &payload_id, &bandwidth) == 2) { sdp_mangled_stream << sdp_line << "\n"; + opus_payload = payload_id; + } + else if (sdp_line.rfind(std::format("a=fmtp:{}", opus_payload)) == 0) + { + sdp_mangled_stream << sdp_line << "a=fmtp:" << opus_payload + << " stereo=1;sprop-stereo=0;minptime=10;useinbandfec=1;maxplaybackrate=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()); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 4c687b5807..a1e63c29cf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2208,10 +2208,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) { audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); } - uint32_t uint_audio_level = (uint32_t) (audio_level * 256); + uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); if (uint_audio_level != mAudioLevel) { - root["p"] = uint_audio_level; + root["p"] = uint_audio_level; mAudioLevel = uint_audio_level; participantStatePtr_t participant = findParticipantByID(gAgentID); if (participant) @@ -2530,7 +2530,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) { removeParticipantByID(agent_id); } - F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 128; // convert to decibles participant->mPower = energyRMS; /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we @@ -4487,13 +4487,10 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { F32 result = 0; - if (!mMuteMic) + participantStatePtr_t participant(findParticipantByID(id)); + if (participant) { - participantStatePtr_t participant(findParticipantByID(id)); - if (participant) - { - result = participant->mPower * 4; - } + result = participant->mPower * 3.5; } return result; } -- cgit v1.2.3 From f6f45bafc60f0f275d7e0e5aee81ea2610ac5352 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 26 Sep 2023 19:44:10 -0700 Subject: fix osx build incompatibility --- indra/llwebrtc/llwebrtc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index e3fd68d35b..b02354cbac 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -674,7 +674,7 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) std::istringstream sdp_stream(sdp); std::ostringstream sdp_mangled_stream; std::string sdp_line; - int opus_payload = 0; + char opus_payload[10]; while (std::getline(sdp_stream, sdp_line)) { int bandwidth = 0; int payload_id = 0; @@ -682,9 +682,9 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) if (std::sscanf(sdp_line.c_str(), "a=rtpmap:%i opus/%i/2", &payload_id, &bandwidth) == 2) { sdp_mangled_stream << sdp_line << "\n"; - opus_payload = payload_id; + sprintf(opus_payload,"%d",payload_id); } - else if (sdp_line.rfind(std::format("a=fmtp:{}", opus_payload)) == 0) + else if (sdp_line.rfind(std::string("a=fmtp:") + opus_payload) == 0) { sdp_mangled_stream << sdp_line << "a=fmtp:" << opus_payload << " stereo=1;sprop-stereo=0;minptime=10;useinbandfec=1;maxplaybackrate=48000\n"; -- cgit v1.2.3 From 999fb768092f12fc89d85f532d4a9d2b7d311d10 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 27 Sep 2023 18:16:57 -0700 Subject: add stereo support --- indra/llwebrtc/llwebrtc.cpp | 87 +++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 31 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index b02354cbac..3152e1eef6 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -106,6 +106,7 @@ void LLWebRTCImpl::init() std::unique_ptr(mAudioDeviceObserver)); mDeviceModule->Init(); mDeviceModule->SetStereoRecording(false); + mDeviceModule->SetStereoPlayout(true); mDeviceModule->EnableBuiltInAEC(false); updateDevices(); }); @@ -321,6 +322,8 @@ bool LLWebRTCImpl::initializeConnectionThreaded() 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->ApplyConfig(apm_config); @@ -416,8 +419,8 @@ bool LLWebRTCImpl::initializeConnectionThreaded() params.codecs.push_back(codecparam); receiver->SetParameters(params); } - - mPeerConnection->SetLocalDescription(rtc::scoped_refptr(this)); + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions; + mPeerConnection->CreateOffer(this, offerOptions); RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); @@ -516,6 +519,16 @@ void LLWebRTCImpl::OnAddTrack(rtc::scoped_refptr const std::vector> &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 LLWebRTCImpl::OnRemoveTrack(rtc::scoped_refptr receiver) @@ -632,11 +645,47 @@ void LLWebRTCImpl::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; + int opus_payload = 0; + 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) + { + sdp_mangled_stream << sdp_line << "\n"; + opus_payload = payload_id; + } + else if (sdp_line.rfind(std::format("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\n"; + } + else + { + sdp_mangled_stream << sdp_line << "\n"; + } + } + + webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str()); + + + + mPeerConnection->SetLocalDescription(std::unique_ptr( + webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str())), + rtc::scoped_refptr(this)); + RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_mangled_stream.str(); + - RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); for (auto &observer : mSignalingObserverList) { - observer->OnOfferAvailable(sdp); + observer->OnOfferAvailable(sdp_mangled_stream.str()); } } @@ -669,36 +718,12 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) auto desc = mPeerConnection->pending_local_description(); std::string sdp; desc->ToString(&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; - char opus_payload[10]; - 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) - { - sdp_mangled_stream << sdp_line << "\n"; - sprintf(opus_payload,"%d",payload_id); - } - else if (sdp_line.rfind(std::string("a=fmtp:") + opus_payload) == 0) - { - sdp_mangled_stream << sdp_line << "a=fmtp:" << opus_payload - << " stereo=1;sprop-stereo=0;minptime=10;useinbandfec=1;maxplaybackrate=48000\n"; - } - else - { - sdp_mangled_stream << sdp_line << "\n"; - } - } - RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_mangled_stream.str(); + + RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp; ; for (auto &observer : mSignalingObserverList) { - observer->OnOfferAvailable(sdp_mangled_stream.str()); + observer->OnOfferAvailable(sdp); } } -- cgit v1.2.3 From c5796c225dc98190bb2372780849822d85455618 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 27 Sep 2023 18:42:22 -0700 Subject: fix mac build --- indra/llwebrtc/llwebrtc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 3152e1eef6..481d5b940d 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -651,7 +651,7 @@ void LLWebRTCImpl::OnSuccess(webrtc::SessionDescriptionInterface *desc) std::istringstream sdp_stream(sdp); std::ostringstream sdp_mangled_stream; std::string sdp_line; - int opus_payload = 0; + std::string opus_payload; while (std::getline(sdp_stream, sdp_line)) { int bandwidth = 0; @@ -660,9 +660,9 @@ void LLWebRTCImpl::OnSuccess(webrtc::SessionDescriptionInterface *desc) if (std::sscanf(sdp_line.c_str(), "a=rtpmap:%i opus/%i/2", &payload_id, &bandwidth) == 2) { sdp_mangled_stream << sdp_line << "\n"; - opus_payload = payload_id; + opus_payload = std::to_string(payload_id); } - else if (sdp_line.rfind(std::format("a=fmtp:{}", opus_payload)) == 0) + 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\n"; -- cgit v1.2.3 From 4359b574e8ed505b3f4019f70823e34cf571ba76 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 28 Sep 2023 10:55:13 -0700 Subject: Transmit position and power when joining --- indra/newview/llvoicewebrtc.cpp | 101 +++++++++++++++++++++------------------- indra/newview/llvoicewebrtc.h | 4 +- 2 files changed, 55 insertions(+), 50 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a1e63c29cf..ccaa02b704 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1124,7 +1124,7 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession // tell peers that this participant has joined. Json::FastWriter writer; - Json::Value root; + Json::Value root = getPositionAndVolumeUpdateJson(true); root["j"] = true; std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); @@ -2167,60 +2167,63 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) } } -void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) -{ - +Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) +{ + Json::Value root = Json::objectValue; - if (mWebRTCDataInterface && mWebRTCAudioInterface) + if ((mSpatialCoordsDirty || force) && inSpatialChannel()) { - Json::Value root = Json::objectValue; - - if (mSpatialCoordsDirty && inSpatialChannel()) - { - root["sp"] = Json::objectValue; - root["sp"]["x"] = (int)(mAvatarPosition[0]*100); - root["sp"]["y"] = (int)(mAvatarPosition[1]*100); - root["sp"]["z"] = (int)(mAvatarPosition[2]*100); - root["sh"] = Json::objectValue; - root["sh"]["x"] = (int)(mAvatarRot[0]*100); - root["sh"]["y"] = (int)(mAvatarRot[1]*100); - root["sh"]["z"] = (int)(mAvatarRot[2]*100); - root["sh"]["w"] = (int)(mAvatarRot[3]*100); + root["sp"] = Json::objectValue; + root["sp"]["x"] = (int) (mAvatarPosition[0] * 100); + root["sp"]["y"] = (int) (mAvatarPosition[1] * 100); + root["sp"]["z"] = (int) (mAvatarPosition[2] * 100); + root["sh"] = Json::objectValue; + root["sh"]["x"] = (int) (mAvatarRot[0] * 100); + root["sh"]["y"] = (int) (mAvatarRot[1] * 100); + root["sh"]["z"] = (int) (mAvatarRot[2] * 100); + root["sh"]["w"] = (int) (mAvatarRot[3] * 100); + + root["lp"] = Json::objectValue; + root["lp"]["x"] = (int) (mCameraPosition[0] * 100); + root["lp"]["y"] = (int) (mCameraPosition[1] * 100); + root["lp"]["z"] = (int) (mCameraPosition[2] * 100); + root["lh"] = Json::objectValue; + root["lh"]["x"] = (int) (mCameraRot[0] * 100); + root["lh"]["y"] = (int) (mCameraRot[1] * 100); + root["lh"]["z"] = (int) (mCameraRot[2] * 100); + root["lh"]["w"] = (int) (mCameraRot[3] * 100); + + mSpatialCoordsDirty = false; + } - - root["lp"] = Json::objectValue; - root["lp"]["x"] = (int)(mCameraPosition[0]*100); - root["lp"]["y"] = (int)(mCameraPosition[1]*100); - root["lp"]["z"] = (int)(mCameraPosition[2]*100); - root["lh"] = Json::objectValue; - root["lh"]["x"] = (int)(mCameraRot[0]*100); - root["lh"]["y"] = (int)(mCameraRot[1]*100); - root["lh"]["z"] = (int)(mCameraRot[2]*100); - root["lh"]["w"] = (int)(mCameraRot[3]*100); + F32 audio_level = 0.0; - mSpatialCoordsDirty = false; - } - - - F32 audio_level = 0.0; - - if (!mMuteMic) - { - audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); - } - uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); - if (uint_audio_level != mAudioLevel) + if (!mMuteMic) + { + audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); + } + uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); + if (force || (uint_audio_level != mAudioLevel)) + { + root["p"] = uint_audio_level; + mAudioLevel = uint_audio_level; + participantStatePtr_t participant = findParticipantByID(gAgentID); + if (participant) { - root["p"] = uint_audio_level; - mAudioLevel = uint_audio_level; - participantStatePtr_t participant = findParticipantByID(gAgentID); - if (participant) - { - participant->mPower = audio_level; - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; - } + participant->mPower = audio_level; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } - + } + return root; +} + +void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate() +{ + + + if (mWebRTCDataInterface && mWebRTCAudioInterface) + { + Json::Value root = getPositionAndVolumeUpdateJson(false); if (root.size() > 0) { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 1d013c6609..bd4346022f 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -40,6 +40,7 @@ class LLWebRTCProtocolParser; #include "lleventcoro.h" #include "llcoros.h" #include +#include "json/reader.h" #ifdef LL_USESYSTEMLIBS # include "expat.h" @@ -806,7 +807,8 @@ private: std::string getAudioSessionHandle(); void setHidden(bool hidden) override; //virtual - void sendPositionAndVolumeUpdate(void); + Json::Value getPositionAndVolumeUpdateJson(bool force); + void sendPositionAndVolumeUpdate(); void sendFriendsListUpdates(); -- cgit v1.2.3 From bdf6c299754fef09ab6d836888931588174b6e89 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 28 Sep 2023 15:33:42 -0700 Subject: when a peer joins, notify them of position and heading --- indra/newview/llvoicewebrtc.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index ccaa02b704..d97d4847ee 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2514,6 +2514,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) LL_WARNS("Voice") << "Expected object from data channel:" << data << LL_ENDL; return; } + bool new_participant = false; for (auto &participant_id : voice_data.getMemberNames()) { LLUUID agent_id(participant_id); @@ -2525,6 +2526,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) participantStatePtr_t participant = findParticipantByID(agent_id); if (!participant && voice_data[participant_id].get("j", Json::Value(false)).asBool()) { + new_participant = true; participant = addParticipantByID(agent_id); } if (participant) @@ -2538,9 +2540,16 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) participant->mPower = energyRMS; /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we can use it at some point when we actually process frames. */ - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } } + if (new_participant) + { + Json::FastWriter writer; + Json::Value root = getPositionAndVolumeUpdateJson(true); + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); + } } } @@ -4493,7 +4502,7 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(findParticipantByID(id)); if (participant) { - result = participant->mPower * 3.5; + result = participant->mPower * 3.0; } return result; } -- cgit v1.2.3 From ec3ea3b5723e6f52ca3caf51547983568a3781c3 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 29 Sep 2023 01:40:20 -0700 Subject: tweak position transmission decision --- indra/newview/llvoicewebrtc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d97d4847ee..8962b4de09 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2524,9 +2524,10 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) continue; } participantStatePtr_t participant = findParticipantByID(agent_id); - if (!participant && voice_data[participant_id].get("j", Json::Value(false)).asBool()) + bool joined = voice_data[participant_id].get("j", Json::Value(false)).asBool(); + new_participant |= joined; + if (!participant && joined) { - new_participant = true; participant = addParticipantByID(agent_id); } if (participant) -- cgit v1.2.3 From 5872e037e801654d59f37e32f672db84c456d587 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 29 Sep 2023 19:13:03 -0700 Subject: Improve reconnection logic and allow device setting when connected or not connected --- indra/llwebrtc/llwebrtc.cpp | 268 +++++++++++++++++++++++++--------------- indra/llwebrtc/llwebrtc.h | 5 +- indra/llwebrtc/llwebrtc_impl.h | 17 ++- indra/newview/llvoicewebrtc.cpp | 10 +- 4 files changed, 189 insertions(+), 111 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 481d5b940d..95f87ed65b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -42,15 +42,15 @@ const float VOLUME_SCALE_WEBRTC = 3.0f; LLAudioDeviceObserver::LLAudioDeviceObserver() : mMicrophoneEnergy(0.0), mSumVector {0} {} -double LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } +float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } 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) + const size_t num_samples, + const size_t bytes_per_sample, + const size_t num_channels, + const uint32_t samples_per_sec) { - float energy = 0; + float energy = 0; const short *samples = (const short *) audio_samples; for (size_t index = 0; index < num_samples * num_channels; index++) { @@ -60,8 +60,8 @@ void LLAudioDeviceObserver::OnCaptureData(const void *audio_samples, // smooth it. size_t buffer_size = sizeof(mSumVector) / sizeof(mSumVector[0]); - float totalSum = 0; - int i; + float totalSum = 0; + int i; for (i = 0; i < (buffer_size - 1); i++) { mSumVector[i] = mSumVector[i + 1]; @@ -73,16 +73,17 @@ void LLAudioDeviceObserver::OnCaptureData(const void *audio_samples, } 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) + const size_t num_samples, + const size_t bytes_per_sample, + const size_t num_channels, + const uint32_t samples_per_sec) { } - void LLWebRTCImpl::init() { + mPlayoutDevice = -1; + mRecordingDevice = -1; mAnswerReceived = false; rtc::InitializeSSL(); mTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory(); @@ -100,20 +101,21 @@ void LLWebRTCImpl::init() mWorkerThread->PostTask( [this]() { - mAudioDeviceObserver = new LLAudioDeviceObserver; - mDeviceModule = webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, - mTaskQueueFactory.get(), - std::unique_ptr(mAudioDeviceObserver)); - mDeviceModule->Init(); - mDeviceModule->SetStereoRecording(false); - mDeviceModule->SetStereoPlayout(true); - mDeviceModule->EnableBuiltInAEC(false); + mTuningAudioDeviceObserver = new LLAudioDeviceObserver; + mTuningDeviceModule = + webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, + mTaskQueueFactory.get(), + std::unique_ptr(mTuningAudioDeviceObserver)); + mTuningDeviceModule->Init(); + mTuningDeviceModule->SetStereoRecording(false); + mTuningDeviceModule->SetStereoPlayout(true); + mTuningDeviceModule->EnableBuiltInAEC(false); updateDevices(); }); } -void LLWebRTCImpl::terminate() -{ +void LLWebRTCImpl::terminate() +{ mSignalingThread->BlockingCall( [this]() { @@ -127,13 +129,17 @@ void LLWebRTCImpl::terminate() mWorkerThread->BlockingCall( [this]() { - if (mDeviceModule) + if (mTuningDeviceModule) { - mDeviceModule->Terminate(); + mTuningDeviceModule->Terminate(); } - mDeviceModule = nullptr; - mTaskQueueFactory = nullptr; - + if (mPeerDeviceModule) + { + mPeerDeviceModule->Terminate(); + } + mTuningDeviceModule = nullptr; + mPeerDeviceModule = nullptr; + mTaskQueueFactory = nullptr; }); mNetworkThread->BlockingCall( [this]() @@ -146,7 +152,6 @@ void LLWebRTCImpl::terminate() }); } - void LLWebRTCImpl::refreshDevices() { mWorkerThread->PostTask([this]() { updateDevices(); }); @@ -169,32 +174,50 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id) mWorkerThread->PostTask( [this, id]() { - bool was_recording = mDeviceModule->Recording(); + bool was_tuning_recording = mTuningDeviceModule->Recording(); - if (was_recording) + if (was_tuning_recording) { - mDeviceModule->StopRecording(); + mTuningDeviceModule->StopRecording(); } - int16_t captureDeviceCount = mDeviceModule->RecordingDevices(); - int16_t index = 0; /* default to first one if no match */ + bool was_peer_recording = false; + if (mPeerDeviceModule) + { + was_peer_recording = mPeerDeviceModule->Recording(); + if (was_peer_recording) + { + mPeerDeviceModule->StopRecording(); + } + } + int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); for (int16_t i = 0; i < captureDeviceCount; i++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; - mDeviceModule->RecordingDeviceName(i, name, guid); - if (id == guid || id == "Default") + 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; - index = i; + mRecordingDevice = i; break; } } - mDeviceModule->SetRecordingDevice(index); - mDeviceModule->InitMicrophone(); - mDeviceModule->InitRecording(); - if (was_recording) + mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); + mTuningDeviceModule->InitMicrophone(); + mTuningDeviceModule->InitRecording(); + if (was_tuning_recording) { - mDeviceModule->StartRecording(); + mTuningDeviceModule->StartRecording(); + } + if (mPeerDeviceModule) + { + mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); + mPeerDeviceModule->InitMicrophone(); + mPeerDeviceModule->InitRecording(); + if (was_peer_recording) + { + mPeerDeviceModule->StartRecording(); + } } }); } @@ -204,54 +227,72 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) mWorkerThread->PostTask( [this, id]() { - bool was_playing = mDeviceModule->Playing(); - if (was_playing) + bool was_tuning_playing = mTuningDeviceModule->Playing(); + if (was_tuning_playing) { - mDeviceModule->StopPlayout(); + mTuningDeviceModule->StopPlayout(); + } + bool was_peer_playing = false; + if (mPeerDeviceModule) + { + was_peer_playing = mPeerDeviceModule->Playing(); + if (was_tuning_playing) + { + mPeerDeviceModule->StopPlayout(); + } } - int16_t renderDeviceCount = mDeviceModule->PlayoutDevices(); - int16_t index = 0; /* default to first one if no match */ + int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); for (int16_t i = 0; i < renderDeviceCount; i++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; - mDeviceModule->PlayoutDeviceName(i, name, guid); + mTuningDeviceModule->PlayoutDeviceName(i, name, guid); if (id == guid || id == "Default") { RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; - index = i; + mPlayoutDevice = i; break; } } - mDeviceModule->SetPlayoutDevice(index); - mDeviceModule->InitSpeaker(); - mDeviceModule->InitPlayout(); - if (was_playing) + mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mTuningDeviceModule->InitSpeaker(); + mTuningDeviceModule->InitPlayout(); + if (was_tuning_playing) { - mDeviceModule->StartPlayout(); + mTuningDeviceModule->StartPlayout(); + } + if (mPeerDeviceModule) + { + mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mPeerDeviceModule->InitSpeaker(); + mPeerDeviceModule->InitPlayout(); + if (was_peer_playing) + { + mPeerDeviceModule->StartPlayout(); + } } }); } void LLWebRTCImpl::updateDevices() { - int16_t renderDeviceCount = mDeviceModule->PlayoutDevices(); + int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); LLWebRTCVoiceDeviceList renderDeviceList; for (int16_t index = 0; index < renderDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; - mDeviceModule->PlayoutDeviceName(index, name, guid); + mTuningDeviceModule->PlayoutDeviceName(index, name, guid); renderDeviceList.emplace_back(name, guid); } - int16_t captureDeviceCount = mDeviceModule->RecordingDevices(); + int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); LLWebRTCVoiceDeviceList captureDeviceList; for (int16_t index = 0; index < captureDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; - mDeviceModule->RecordingDeviceName(index, name, guid); + mTuningDeviceModule->RecordingDeviceName(index, name, guid); captureDeviceList.emplace_back(name, guid); } for (auto &observer : mVoiceDevicesObserverList) @@ -267,14 +308,22 @@ void LLWebRTCImpl::setTuningMode(bool enable) { if (enable) { - mDeviceModule->InitMicrophone(); - mDeviceModule->InitRecording(); - mDeviceModule->StartRecording(); - mDeviceModule->SetMicrophoneMute(false); + mTuningDeviceModule->StartRecording(); + mTuningDeviceModule->SetMicrophoneMute(false); + if (mPeerDeviceModule) + { + mPeerDeviceModule->StopRecording(); + mPeerDeviceModule->StopPlayout(); + } } else { - mDeviceModule->StopRecording(); + mTuningDeviceModule->StopRecording(); + if (mPeerDeviceModule) + { + mPeerDeviceModule->StartPlayout(); + mPeerDeviceModule->StartRecording(); + } } }); } @@ -295,42 +344,58 @@ void LLWebRTCImpl::unsetSignalingObserver(LLWebRTCSignalingObserver *observer) } } - bool LLWebRTCImpl::initializeConnection() { RTC_DCHECK(!mPeerConnection); RTC_DCHECK(mPeerConnectionFactory); - mAnswerReceived = false; + mAnswerReceived = false; mSignalingThread->PostTask([this]() { initializeConnectionThreaded(); }); return true; } - - bool LLWebRTCImpl::initializeConnectionThreaded() { rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); - webrtc::AudioProcessing::Config apm_config; - 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; - 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; + webrtc::AudioProcessing::Config apm_config; + 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; + 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->ApplyConfig(apm_config); + mWorkerThread->BlockingCall( + [this]() + { + mPeerAudioDeviceObserver = new LLAudioDeviceObserver; + mPeerDeviceModule = + webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, + mTaskQueueFactory.get(), + std::unique_ptr(mPeerAudioDeviceObserver)); + mPeerDeviceModule->Init(); + mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); + mPeerDeviceModule->SetStereoRecording(false); + mPeerDeviceModule->SetStereoPlayout(true); + mPeerDeviceModule->EnableBuiltInAEC(false); + mPeerDeviceModule->InitMicrophone(); + mPeerDeviceModule->InitSpeaker(); + mPeerDeviceModule->InitRecording(); + mPeerDeviceModule->InitPlayout(); + }); + mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(), mWorkerThread.get(), mSignalingThread.get(), - mDeviceModule, + mPeerDeviceModule, webrtc::CreateBuiltinAudioEncoderFactory(), webrtc::CreateBuiltinAudioDecoderFactory(), nullptr /* video_encoder_factory */, @@ -406,9 +471,9 @@ bool LLWebRTCImpl::initializeConnectionThreaded() } auto receivers = mPeerConnection->GetReceivers(); - for (auto& receiver : receivers) + for (auto &receiver : receivers) { - webrtc::RtpParameters params; + webrtc::RtpParameters params; webrtc::RtpCodecParameters codecparam; codecparam.name = "opus"; codecparam.kind = cricket::MEDIA_TYPE_AUDIO; @@ -423,19 +488,23 @@ bool LLWebRTCImpl::initializeConnectionThreaded() mPeerConnection->CreateOffer(this, offerOptions); RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); - + return true; } void LLWebRTCImpl::shutdownConnection() { + mDataChannel->Close(); + mDataChannel = nullptr; + mPeerConnection->Close(); + mPeerDeviceModule->Terminate(); + mPeerDeviceModule = nullptr; mPeerConnection = nullptr; mPeerConnectionFactory = nullptr; } void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) { - RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp; mSignalingThread->PostTask( @@ -450,7 +519,7 @@ void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) for (auto &candidate : mCachedIceCandidates) { LLWebRTCIceCandidate ice_candidate; - ice_candidate.candidate = candidate->candidate().ToString(); + ice_candidate.candidate = candidate->candidate().ToString(); ice_candidate.mline_index = candidate->sdp_mline_index(); ice_candidate.sdp_mid = candidate->sdp_mid(); observer->OnIceCandidate(ice_candidate); @@ -470,14 +539,13 @@ void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) void LLWebRTCImpl::setMute(bool mute) { mSignalingThread->PostTask( - [this,mute]() + [this, mute]() { auto senders = mPeerConnection->GetSenders(); - RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " - << senders.size(); + RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); - for (auto& sender : senders) + for (auto &sender : senders) { sender->track()->set_enabled(!mute); } @@ -497,19 +565,15 @@ void LLWebRTCImpl::setSpeakerVolume(float volume) webrtc::MediaStreamTrackInterface *track = receiver->track().get(); if (track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { - webrtc::AudioTrackInterface* audio_track = static_cast(track); - webrtc::AudioSourceInterface* source = audio_track->GetSource(); + webrtc::AudioTrackInterface *audio_track = static_cast(track); + webrtc::AudioSourceInterface *source = audio_track->GetSource(); source->SetVolume(VOLUME_SCALE_WEBRTC * volume); - } } }); } -double LLWebRTCImpl::getAudioLevel() -{ - return 20*mAudioDeviceObserver->getMicrophoneEnergy(); -} +float LLWebRTCImpl::getTuningAudioLevel() { return 20 * mTuningAudioDeviceObserver->getMicrophoneEnergy(); } // // PeerConnectionObserver implementation. @@ -519,7 +583,7 @@ void LLWebRTCImpl::OnAddTrack(rtc::scoped_refptr const std::vector> &streams) { RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); - webrtc::RtpParameters params; + webrtc::RtpParameters params; webrtc::RtpCodecParameters codecparam; codecparam.name = "opus"; codecparam.kind = cricket::MEDIA_TYPE_AUDIO; @@ -539,10 +603,9 @@ void LLWebRTCImpl::OnRemoveTrack(rtc::scoped_refptr channel) { mDataChannel = channel; - channel->RegisterObserver(this); + channel->RegisterObserver(this); } - void LLWebRTCImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) { LLWebRTCSignalingObserver::IceGatheringState webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; @@ -584,8 +647,8 @@ void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConne if (new_state == webrtc::PeerConnectionInterface::PeerConnectionState::kConnected) { mWorkerThread->PostTask([this]() { - mDeviceModule->StartRecording(); - mDeviceModule->StartPlayout(); + mPeerDeviceModule->StartRecording(); + mPeerDeviceModule->StartPlayout(); for (auto &observer : mSignalingObserverList) { observer->OnAudioEstablished(this); @@ -595,6 +658,7 @@ void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConne break; } case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: + case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: { for (auto &observer : mSignalingObserverList) { @@ -738,6 +802,8 @@ void LLWebRTCImpl::unsetAudioObserver(LLWebRTCAudioObserver *observer) } } +float LLWebRTCImpl::getAudioLevel() { return 20 * mPeerAudioDeviceObserver->getMicrophoneEnergy(); } + // // DataChannelObserver implementation // diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 30c0d63c55..9224e88dfa 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -86,7 +86,7 @@ class LLWebRTCDeviceInterface virtual void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; virtual void setTuningMode(bool enable) = 0; - virtual double getAudioLevel() = 0; + virtual float getTuningAudioLevel() = 0; }; class LLWebRTCAudioObserver @@ -100,7 +100,8 @@ class LLWebRTCAudioInterface virtual void setAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void unsetAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void setMute(bool mute) = 0; - virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 = 0; + virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 + virtual float getAudioLevel() = 0; }; class LLWebRTCDataObserver diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 70586f9013..947b417923 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -68,7 +68,7 @@ class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver public: LLAudioDeviceObserver(); - double getMicrophoneEnergy(); + float getMicrophoneEnergy(); void OnCaptureData(const void *audio_samples, const size_t num_samples, @@ -100,7 +100,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, { public: LLWebRTCImpl() : - mAudioDeviceObserver(nullptr) + mTuningAudioDeviceObserver(nullptr), mPeerAudioDeviceObserver(nullptr) { } ~LLWebRTCImpl() {} @@ -121,7 +121,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void setRenderDevice(const std::string& id) override; void setTuningMode(bool enable) override; - double getAudioLevel() override; + float getTuningAudioLevel() override; // // LLWebRTCSignalInterface @@ -141,6 +141,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void unsetAudioObserver(LLWebRTCAudioObserver *observer) override; void setMute(bool mute) override; void setSpeakerVolume(float folume) override; // range 0.0-1.0 + float getAudioLevel() override; // // LLWebRTCDataInterface @@ -201,10 +202,16 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // Devices void updateDevices(); - rtc::scoped_refptr mDeviceModule; + rtc::scoped_refptr mTuningDeviceModule; + rtc::scoped_refptr mPeerDeviceModule; std::vector mVoiceDevicesObserverList; - LLAudioDeviceObserver * mAudioDeviceObserver; + // accessors in webrtc aren't apparently implemented yet. + int32_t mPlayoutDevice; + int32_t mRecordingDevice; + + LLAudioDeviceObserver * mTuningAudioDeviceObserver; + LLAudioDeviceObserver * mPeerAudioDeviceObserver; // signaling std::vector mSignalingObserverList; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 8962b4de09..b9e5c7a8b2 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -884,6 +884,7 @@ bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) LLSD body; body["logout"] = TRUE; httpAdapter->postAndSuspend(httpRequest, url, body); + mWebRTCSignalingInterface->shutdownConnection(); return true; } @@ -1265,6 +1266,7 @@ bool LLWebRTCVoiceClient::waitForChannel() if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) { + mIsProcessingChannels = false; return true; } @@ -2107,7 +2109,7 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return mWebRTCDeviceInterface->getAudioLevel(); + return mWebRTCDeviceInterface->getTuningAudioLevel(); } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -2200,7 +2202,7 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) if (!mMuteMic) { - audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); + audio_level = (F32) mWebRTCAudioInterface->getAudioLevel(); } uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); if (force || (uint_audio_level != mAudioLevel)) @@ -2564,6 +2566,8 @@ void LLWebRTCVoiceClient::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; mRelogRequested = TRUE; + mIsProcessingChannels = FALSE; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } @@ -4503,7 +4507,7 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(findParticipantByID(id)); if (participant) { - result = participant->mPower * 3.0; + result = participant->mPower * 2.0; } return result; } -- cgit v1.2.3 From 9d070201fe57e22db7cf55a2160a82e3c6413471 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 3 Oct 2023 15:51:37 -0700 Subject: fix device selection while speaking. --- indra/llwebrtc/llwebrtc.cpp | 107 +++++++++++++++++++++------------------- indra/llwebrtc/llwebrtc_impl.h | 3 +- indra/newview/llvoicewebrtc.cpp | 2 +- 3 files changed, 58 insertions(+), 54 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 95f87ed65b..eda25a3641 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -131,10 +131,12 @@ void LLWebRTCImpl::terminate() { if (mTuningDeviceModule) { + mTuningDeviceModule->StopRecording(); mTuningDeviceModule->Terminate(); } if (mPeerDeviceModule) { + mPeerDeviceModule->StopRecording(); mPeerDeviceModule->Terminate(); } mTuningDeviceModule = nullptr; @@ -174,43 +176,32 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id) mWorkerThread->PostTask( [this, id]() { - bool was_tuning_recording = mTuningDeviceModule->Recording(); - - if (was_tuning_recording) - { - mTuningDeviceModule->StopRecording(); - } - bool was_peer_recording = false; - if (mPeerDeviceModule) - { - was_peer_recording = mPeerDeviceModule->Recording(); - if (was_peer_recording) - { - mPeerDeviceModule->StopRecording(); - } - } 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 + 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; } } + mTuningDeviceModule->StopRecording(); mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); mTuningDeviceModule->InitMicrophone(); mTuningDeviceModule->InitRecording(); - if (was_tuning_recording) - { - mTuningDeviceModule->StartRecording(); - } + mTuningDeviceModule->StartRecording(); + bool was_peer_recording = false; if (mPeerDeviceModule) { + was_peer_recording = mPeerDeviceModule->Recording(); + if (was_peer_recording) + { + mPeerDeviceModule->StopRecording(); + } mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); mPeerDeviceModule->InitMicrophone(); mPeerDeviceModule->InitRecording(); @@ -227,20 +218,6 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) mWorkerThread->PostTask( [this, id]() { - bool was_tuning_playing = mTuningDeviceModule->Playing(); - if (was_tuning_playing) - { - mTuningDeviceModule->StopPlayout(); - } - bool was_peer_playing = false; - if (mPeerDeviceModule) - { - was_peer_playing = mPeerDeviceModule->Playing(); - if (was_tuning_playing) - { - mPeerDeviceModule->StopPlayout(); - } - } int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); for (int16_t i = 0; i < renderDeviceCount; i++) { @@ -254,6 +231,22 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) break; } } + mTuningDeviceModule->SetSpeakerMute(true); + bool was_tuning_playing = mTuningDeviceModule->Playing(); + if (was_tuning_playing) + { + mTuningDeviceModule->StopPlayout(); + } + bool was_peer_mute = false; + if (mPeerDeviceModule) + { + mPeerDeviceModule->SpeakerMute(&was_peer_mute); + if (!was_peer_mute) + { + mPeerDeviceModule->SetSpeakerMute(true); + } + } + mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); mTuningDeviceModule->InitSpeaker(); mTuningDeviceModule->InitPlayout(); @@ -266,11 +259,11 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); mPeerDeviceModule->InitSpeaker(); mPeerDeviceModule->InitPlayout(); - if (was_peer_playing) - { - mPeerDeviceModule->StartPlayout(); - } + mPeerDeviceModule->StartPlayout(); + mPeerDeviceModule->SetSpeakerMute(was_peer_mute); + } + mTuningDeviceModule->SetSpeakerMute(false); }); } @@ -303,29 +296,42 @@ void LLWebRTCImpl::updateDevices() void LLWebRTCImpl::setTuningMode(bool enable) { - mWorkerThread->PostTask( + mWorkerThread->BlockingCall( [this, enable]() { if (enable) { + mTuningDeviceModule->StartRecording(); mTuningDeviceModule->SetMicrophoneMute(false); + + + mTuningDeviceModule->SetSpeakerMute(false); + if (mPeerDeviceModule) { mPeerDeviceModule->StopRecording(); - mPeerDeviceModule->StopPlayout(); + mPeerDeviceModule->SetSpeakerMute(true); } } else { - mTuningDeviceModule->StopRecording(); if (mPeerDeviceModule) { - mPeerDeviceModule->StartPlayout(); mPeerDeviceModule->StartRecording(); + mPeerDeviceModule->SetSpeakerMute(false); } } }); + // 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 ? false : !mMute); + } + } } // @@ -538,18 +544,15 @@ void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) void LLWebRTCImpl::setMute(bool mute) { - mSignalingThread->PostTask( - [this, mute]() - { - auto senders = mPeerConnection->GetSenders(); + mMute = mute; + auto senders = mPeerConnection->GetSenders(); - RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); + RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); - for (auto &sender : senders) - { - sender->track()->set_enabled(!mute); - } - }); + for (auto &sender : senders) + { + sender->track()->set_enabled(!mMute); + } } void LLWebRTCImpl::setSpeakerVolume(float volume) diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 947b417923..24edd6eaa2 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -100,7 +100,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, { public: LLWebRTCImpl() : - mTuningAudioDeviceObserver(nullptr), mPeerAudioDeviceObserver(nullptr) + mTuningAudioDeviceObserver(nullptr), mPeerAudioDeviceObserver(nullptr), mMute(true) { } ~LLWebRTCImpl() {} @@ -209,6 +209,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // accessors in webrtc aren't apparently implemented yet. int32_t mPlayoutDevice; int32_t mRecordingDevice; + bool mMute; LLAudioDeviceObserver * mTuningAudioDeviceObserver; LLAudioDeviceObserver * mPeerAudioDeviceObserver; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b9e5c7a8b2..8d929fd36a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2479,7 +2479,6 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * mWebRTCAudioInterface = audio_interface; mWebRTCAudioInterface->setAudioObserver(this); float speaker_volume = 0; - audio_interface->setMute(true); { LLMutexLock lock(&mVoiceStateMutex); speaker_volume = mSpeakerVolume; @@ -4872,6 +4871,7 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceCli void LLWebRTCVoiceClient::sessionEstablished() { + mWebRTCAudioInterface->setMute(mMuteMic); addSession(gAgent.getRegion()->getRegionID().asString()); } -- cgit v1.2.3 From 751ff2d4accdef1f0f2acf8d282dfd91e5af25d9 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 3 Oct 2023 16:04:32 -0700 Subject: hook up listen from avatar vs camera --- indra/newview/llvoicewebrtc.cpp | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 8d929fd36a..3584fa31b2 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2175,6 +2175,27 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) if ((mSpatialCoordsDirty || force) && inSpatialChannel()) { + LLVector3d earPosition; + LLQuaternion earRot; + switch (mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earRot = mCameraRot; + break; + + case earLocAvatar: + earPosition = mAvatarPosition; + earRot = mAvatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earRot = mCameraRot; + break; + } + root["sp"] = Json::objectValue; root["sp"]["x"] = (int) (mAvatarPosition[0] * 100); root["sp"]["y"] = (int) (mAvatarPosition[1] * 100); @@ -2186,14 +2207,14 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) root["sh"]["w"] = (int) (mAvatarRot[3] * 100); root["lp"] = Json::objectValue; - root["lp"]["x"] = (int) (mCameraPosition[0] * 100); - root["lp"]["y"] = (int) (mCameraPosition[1] * 100); - root["lp"]["z"] = (int) (mCameraPosition[2] * 100); + root["lp"]["x"] = (int) (earPosition[0] * 100); + root["lp"]["y"] = (int) (earPosition[1] * 100); + root["lp"]["z"] = (int) (earPosition[2] * 100); root["lh"] = Json::objectValue; - root["lh"]["x"] = (int) (mCameraRot[0] * 100); - root["lh"]["y"] = (int) (mCameraRot[1] * 100); - root["lh"]["z"] = (int) (mCameraRot[2] * 100); - root["lh"]["w"] = (int) (mCameraRot[3] * 100); + root["lh"]["x"] = (int) (earRot[0] * 100); + root["lh"]["y"] = (int) (earRot[1] * 100); + root["lh"]["z"] = (int) (earRot[2] * 100); + root["lh"]["w"] = (int) (earRot[3] * 100); mSpatialCoordsDirty = false; } -- cgit v1.2.3 From c856a52477d0a50521a44f2d8255d5bde2ef39b4 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 6 Oct 2023 21:46:02 -0700 Subject: Fix race in initialization. Fix failure to send ice candidates to janus. --- indra/llwebrtc/llwebrtc.cpp | 95 +++++++++++++++++++++++++---------------- indra/llwebrtc/llwebrtc_impl.h | 2 - indra/newview/llvoicewebrtc.cpp | 21 ++++----- 3 files changed, 69 insertions(+), 49 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index eda25a3641..d8f2a6028c 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -356,12 +356,6 @@ bool LLWebRTCImpl::initializeConnection() RTC_DCHECK(mPeerConnectionFactory); mAnswerReceived = false; - mSignalingThread->PostTask([this]() { initializeConnectionThreaded(); }); - return true; -} - -bool LLWebRTCImpl::initializeConnectionThreaded() -{ rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); webrtc::AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = false; @@ -375,8 +369,6 @@ bool LLWebRTCImpl::initializeConnectionThreaded() apm_config.transient_suppression.enabled = true; apm_config.pipeline.multi_channel_render = true; apm_config.pipeline.multi_channel_capture = true; - // - apm->ApplyConfig(apm_config); mWorkerThread->BlockingCall( [this]() @@ -396,6 +388,8 @@ bool LLWebRTCImpl::initializeConnectionThreaded() mPeerDeviceModule->InitSpeaker(); mPeerDeviceModule->InitRecording(); mPeerDeviceModule->InitPlayout(); + mPeerDeviceModule->StopPlayout(); + mPeerDeviceModule->StopRecording(); }); mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(), @@ -408,9 +402,13 @@ bool LLWebRTCImpl::initializeConnectionThreaded() nullptr /* video_decoder_factory */, nullptr /* audio_mixer */, apm); + apm->ApplyConfig(apm_config); + 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"; @@ -430,6 +428,7 @@ bool LLWebRTCImpl::initializeConnectionThreaded() } else { + RTC_LOG(LS_ERROR) << __FUNCTION__ << "Error creating peer connection: " << error_or_peer_connection.error().message(); shutdownConnection(); return false; } @@ -493,20 +492,47 @@ bool LLWebRTCImpl::initializeConnectionThreaded() webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions; mPeerConnection->CreateOffer(this, offerOptions); - RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); - return true; } void LLWebRTCImpl::shutdownConnection() { - mDataChannel->Close(); - mDataChannel = nullptr; - mPeerConnection->Close(); - mPeerDeviceModule->Terminate(); - mPeerDeviceModule = nullptr; - mPeerConnection = nullptr; - mPeerConnectionFactory = nullptr; + mSignalingThread->PostTask( + [this]() + { + if (mPeerConnection) + { + mPeerConnection->Close(); + mPeerConnection = nullptr; + } + mPeerConnectionFactory = nullptr; + }); + mWorkerThread->PostTask( + [this]() + { + if (mTuningDeviceModule) + { + mTuningDeviceModule->StopRecording(); + mTuningDeviceModule->Terminate(); + } + if (mPeerDeviceModule) + { + mPeerDeviceModule->StopRecording(); + mPeerDeviceModule->Terminate(); + } + mTuningDeviceModule = nullptr; + mPeerDeviceModule = nullptr; + mTaskQueueFactory = nullptr; + }); + mNetworkThread->PostTask( + [this]() + { + if (mDataChannel) + { + mDataChannel->Close(); + mDataChannel = nullptr; + } + }); } void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) @@ -519,26 +545,6 @@ void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), rtc::scoped_refptr(this)); - mAnswerReceived = true; - for (auto &observer : mSignalingObserverList) - { - for (auto &candidate : mCachedIceCandidates) - { - LLWebRTCIceCandidate ice_candidate; - ice_candidate.candidate = candidate->candidate().ToString(); - ice_candidate.mline_index = candidate->sdp_mline_index(); - ice_candidate.sdp_mid = candidate->sdp_mid(); - observer->OnIceCandidate(ice_candidate); - } - mCachedIceCandidates.clear(); - if (mPeerConnection->ice_gathering_state() == webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringComplete) - { - for (auto &observer : mSignalingObserverList) - { - observer->OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE); - } - } - } }); } @@ -769,6 +775,19 @@ void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); return; } + mAnswerReceived = true; + for (auto &observer : mSignalingObserverList) + { + for (auto &candidate : mCachedIceCandidates) + { + LLWebRTCIceCandidate ice_candidate; + ice_candidate.candidate = candidate->candidate().ToString(); + ice_candidate.mline_index = candidate->sdp_mline_index(); + ice_candidate.sdp_mid = candidate->sdp_mid(); + observer->OnIceCandidate(ice_candidate); + } + mCachedIceCandidates.clear(); + } } // @@ -776,6 +795,7 @@ void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) // void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) { +#if 0 RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); if (!error.ok()) { @@ -792,6 +812,7 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) { observer->OnOfferAvailable(sdp); } +#endif } void LLWebRTCImpl::setAudioObserver(LLWebRTCAudioObserver *observer) { mAudioObserverList.emplace_back(observer); } diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 24edd6eaa2..01159604c9 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -190,8 +190,6 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, protected: - bool initializeConnectionThreaded(); - std::unique_ptr mNetworkThread; std::unique_ptr mWorkerThread; std::unique_ptr mSignalingThread; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3584fa31b2..657f587370 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -99,6 +99,7 @@ namespace { const F32 CONNECT_DNS_TIMEOUT = 5.0f; const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; + const S32 PROVISION_WAIT_TIMEOUT_SEC = 5; // Cosine of a "trivially" small angle const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); @@ -587,6 +588,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() LLCoros::set_consuming(true); U32 retry = 0; + U32 provisionWaitTimeout = 0; setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); @@ -653,11 +655,16 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { + provisionWaitTimeout = 0; setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); } break; case VOICE_STATE_SESSION_PROVISION_WAIT: llcoro::suspendUntilTimeout(1.0); + if (provisionWaitTimeout++ > PROVISION_WAIT_TIMEOUT_SEC) + { + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); + } break; case VOICE_STATE_SESSION_RETRY: @@ -2424,16 +2431,17 @@ void LLWebRTCVoiceClient::processIceUpdates() if (mIceCandidates.size()) { - LLSD body; - + LLSD candidates = LLSD::emptyArray(); + body["candidates"] = LLSD::emptyArray(); for (auto &ice_candidate : mIceCandidates) { LLSD body_candidate; body_candidate["sdpMid"] = ice_candidate.sdp_mid; body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; body_candidate["candidate"] = ice_candidate.candidate; - body["candidates"].append(body_candidate); + candidates.append(body_candidate); } + body["candidates"] = candidates; mIceCandidates.clear(); } else if (mIceCompleted) @@ -2566,13 +2574,6 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } } - if (new_participant) - { - Json::FastWriter writer; - Json::Value root = getPositionAndVolumeUpdateJson(true); - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); - } } } -- cgit v1.2.3 From f760869c913df0fe74c9f68eb7b8e22ca19b0b2a Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 10 Oct 2023 09:52:51 -0700 Subject: generate ice candidate with proper formatting --- indra/llwebrtc/llwebrtc.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index d8f2a6028c..b5b820e3da 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -683,6 +683,43 @@ void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConne } } +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(); + } + + return candidate_stream.str(); +} + void LLWebRTCImpl::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) { RTC_LOG(LS_INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); @@ -697,7 +734,7 @@ void LLWebRTCImpl::OnIceCandidate(const webrtc::IceCandidateInterface *candidate for (auto &observer : mSignalingObserverList) { LLWebRTCIceCandidate ice_candidate; - ice_candidate.candidate = candidate->candidate().ToString(); + ice_candidate.candidate = iceCandidateToTrickleString(candidate); ice_candidate.mline_index = candidate->sdp_mline_index(); ice_candidate.sdp_mid = candidate->sdp_mid(); observer->OnIceCandidate(ice_candidate); @@ -781,7 +818,7 @@ void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) for (auto &candidate : mCachedIceCandidates) { LLWebRTCIceCandidate ice_candidate; - ice_candidate.candidate = candidate->candidate().ToString(); + ice_candidate.candidate = iceCandidateToTrickleString(candidate.get()); ice_candidate.mline_index = candidate->sdp_mline_index(); ice_candidate.sdp_mid = candidate->sdp_mid(); observer->OnIceCandidate(ice_candidate); -- cgit v1.2.3 From af67b8f3ff76f4fb380fdc84f1c80869d4bac0a4 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 11 Oct 2023 20:50:06 -0700 Subject: add tcptype to tcp ice candidate strings --- indra/llwebrtc/llwebrtc.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index b5b820e3da..78968fc89b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -716,6 +716,10 @@ static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterfa "raddr " << candidate->candidate().related_address().ipaddr().ToString() << " " << "rport " << candidate->candidate().related_address().PortAsString(); } + if (candidate->candidate().protocol() == "tcp") + { + candidate_stream << " tcptype " << candidate->candidate().tcptype(); + } return candidate_stream.str(); } -- cgit v1.2.3 From c9f77ebeac14908465b59cb063a9c30615480015 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 12 Oct 2023 17:31:02 -0700 Subject: build fix --- indra/newview/llviewerhelp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index 61e70dc820..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, LLMediaCtrl* web) override + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) override { LLViewerHelp* vhelp = LLViewerHelp::getInstance(); if (! vhelp) -- cgit v1.2.3 From 747fae2351b21b27d2c5b469a0ce4adf424b7cbd Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 12 Oct 2023 20:10:06 -0700 Subject: reorder params as the runners have different versions of cmake --- indra/cmake/WebRTC.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 20417ebb41..1c32607766 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -6,20 +6,20 @@ if (WINDOWS) if( ADDRESS_SIZE EQUAL 32 ) FetchContent_Declare( webrtc + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2" + URL_HASH "MD5=0d55e58efceed3fb48085a5f0c58881c" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" - URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2" - URL_HASH "MD5=0d55e58efceed3fb48085a5f0c58881c" ) else ( ADDRESS_SIZE EQUAL 32 ) FetchContent_Declare( webrtc + URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2" + URL_HASH "MD5=dfb692562770dc8c877ebfe4302e2881" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" - URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2" - URL_HASH "MD5=dfb692562770dc8c877ebfe4302e2881" ) endif ( ADDRESS_SIZE EQUAL 32 ) elseif (DARWIN) -- cgit v1.2.3 From 72d66a9a425014d35543e5a145aea1e2df0ececa Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 12 Oct 2023 21:21:50 -0700 Subject: copy llwebrtc.dll to the right place on build. --- indra/newview/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c804f5403c..3f92665805 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1727,7 +1727,7 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/openjp2.dll ${SHARED_LIB_STAGING_DIR}/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/uriparser.dll - ${CMAKE_BINARY_DIR}/llwebrtc/Release/llwebrtc.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 @@ -1794,13 +1794,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) -- cgit v1.2.3 From 85f987128b2094d4bbce177ff9f7654eb7921e49 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 19 Oct 2023 14:50:12 -0700 Subject: quicker turnaround on re-establishing voice when server goes down. --- indra/llwebrtc/llwebrtc.cpp | 25 ++++++---- indra/newview/llvoicewebrtc.cpp | 108 +++++++++++++++++++++++----------------- indra/newview/llvoicewebrtc.h | 5 +- 3 files changed, 80 insertions(+), 58 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 78968fc89b..194c7cd5d4 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -497,7 +497,7 @@ bool LLWebRTCImpl::initializeConnection() void LLWebRTCImpl::shutdownConnection() { - mSignalingThread->PostTask( + mSignalingThread->BlockingCall( [this]() { if (mPeerConnection) @@ -507,24 +507,21 @@ void LLWebRTCImpl::shutdownConnection() } mPeerConnectionFactory = nullptr; }); - mWorkerThread->PostTask( + mWorkerThread->BlockingCall( [this]() { - if (mTuningDeviceModule) - { - mTuningDeviceModule->StopRecording(); - mTuningDeviceModule->Terminate(); - } if (mPeerDeviceModule) { mPeerDeviceModule->StopRecording(); mPeerDeviceModule->Terminate(); } - mTuningDeviceModule = nullptr; mPeerDeviceModule = nullptr; - mTaskQueueFactory = nullptr; + if (mPeerAudioDeviceObserver) + { + mPeerAudioDeviceObserver = nullptr; + } }); - mNetworkThread->PostTask( + mNetworkThread->BlockingCall( [this]() { if (mDataChannel) @@ -694,6 +691,7 @@ static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterfa 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"; @@ -716,6 +714,9 @@ static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterfa "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(); @@ -827,8 +828,10 @@ void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) ice_candidate.sdp_mid = candidate->sdp_mid(); observer->OnIceCandidate(ice_candidate); } - mCachedIceCandidates.clear(); } + mCachedIceCandidates.clear(); + OnIceGatheringChange(mPeerConnection->ice_gathering_state()); + } // diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 657f587370..0aa69dc7e8 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -616,6 +616,10 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { + LLMutexLock lock(&mVoiceStateMutex); + + mTrickling = false; + mIceCompleted = false; setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); } break; @@ -673,7 +677,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() { // We failed to connect, give it a bit time before retrying. retry++; - F32 full_delay = llmin(5.f * (F32) retry, 60.f); + F32 full_delay = llmin(2.f * (F32) retry, 10.f); F32 current_delay = 0.f; LL_INFOS("Voice") << "Voice failed to establish session after " << retry << " tries. Will attempt to reconnect in " << full_delay << " seconds" << LL_ENDL; @@ -686,11 +690,12 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() llcoro::suspendUntilTimeout(1.f); } } - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceControlStateUnless(VOICE_STATE_DISCONNECT); break; case VOICE_STATE_SESSION_ESTABLISHED: { + retry = 0; if (mTuningMode) { performMicTuning(); @@ -2388,11 +2393,20 @@ void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserve { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - if (state == llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) - { - LLMutexLock lock(&mVoiceStateMutex); - mIceCompleted = true; - } + switch (state) + { + case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE: + { + LLMutexLock lock(&mVoiceStateMutex); + mIceCompleted = true; + break; + } + case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW: + { + LLMutexLock lock(&mVoiceStateMutex); + mIceCompleted = false; + } + } } void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) @@ -2425,53 +2439,55 @@ void LLWebRTCVoiceClient::processIceUpdates() LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + bool iceCompleted = false; LLSD body; { LLMutexLock lock(&mVoiceStateMutex); - if (mIceCandidates.size()) + if (!mTrickling) { - LLSD candidates = LLSD::emptyArray(); - body["candidates"] = LLSD::emptyArray(); - for (auto &ice_candidate : mIceCandidates) + if (mIceCandidates.size()) + { + LLSD candidates = LLSD::emptyArray(); + body["candidates"] = LLSD::emptyArray(); + for (auto &ice_candidate : mIceCandidates) + { + LLSD body_candidate; + body_candidate["sdpMid"] = ice_candidate.sdp_mid; + body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; + body_candidate["candidate"] = ice_candidate.candidate; + candidates.append(body_candidate); + } + body["candidates"] = candidates; + mIceCandidates.clear(); + } + else if (mIceCompleted) { LLSD body_candidate; - body_candidate["sdpMid"] = ice_candidate.sdp_mid; - body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; - body_candidate["candidate"] = ice_candidate.candidate; - candidates.append(body_candidate); + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + iceCompleted = mIceCompleted; + mIceCompleted = false; } - body["candidates"] = candidates; - mIceCandidates.clear(); - } - else if (mIceCompleted) - { - LLSD body_candidate; - body_candidate["completed"] = true; - body["candidate"] = body_candidate; - mIceCompleted = false; - } - else - { - return; + else + { + return; + } + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, iceCompleted, _1), + boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); + mTrickling = true; } } - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, _1), - boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, 3, url, body, _1)); } -void LLWebRTCVoiceClient::onIceUpdateComplete(const LLSD& result) -{ - if (sShuttingDown) - { - return; - } -} +void LLWebRTCVoiceClient::onIceUpdateComplete(bool ice_completed, const LLSD& result) +{ mTrickling = false; } -void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result) +void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result) { if (sShuttingDown) { @@ -2482,16 +2498,18 @@ void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD bo if (retries >= 0) { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + 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(&LLWebRTCVoiceClient::onIceUpdateComplete, this, _1), - boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, _1)); + boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, ice_completed, _1), + boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); } else { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); + mTrickling = false; } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index bd4346022f..fd7094cb46 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -275,8 +275,8 @@ public: //@} void processIceUpdates(); - void onIceUpdateComplete(const LLSD& result); - void onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result); + void onIceUpdateComplete(bool ice_completed, const LLSD& result); + void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result); ////////////////////////////// @@ -786,6 +786,7 @@ private: LLVoiceDeviceList mRenderDevices; std::vector mIceCandidates; bool mIceCompleted; + bool mTrickling; uint32_t mAudioLevel; -- cgit v1.2.3 From 459de5087ef6b6d6dddbc25a6bbcae6b02d85530 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 24 Oct 2023 10:13:03 -0700 Subject: OSX build fix --- indra/newview/llvoicewebrtc.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0aa69dc7e8..b4d65bec0a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2406,6 +2406,8 @@ void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserve LLMutexLock lock(&mVoiceStateMutex); mIceCompleted = false; } + default: + break; } } -- cgit v1.2.3 From 85aea763d171ef3f3a92e7a20f9aaa8dfdcbc026 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 8 Nov 2023 10:13:15 -0800 Subject: SL-20543 - voice over region boundaries. This commit includes code to allow the llwebrtc.dll/dylib to allow multiple connections at once. --- indra/llwebrtc/llwebrtc.cpp | 781 ++++++++++++++++++++-------------------- indra/llwebrtc/llwebrtc.h | 24 +- indra/llwebrtc/llwebrtc_impl.h | 144 ++++++-- indra/newview/llvoicewebrtc.cpp | 40 +- indra/newview/llvoicewebrtc.h | 4 +- 5 files changed, 532 insertions(+), 461 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 194c7cd5d4..6eb3478b59 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -1,6 +1,6 @@ /** - * @file llaccordionctrl.cpp - * @brief Accordion panel implementation + * @file llwebrtc.cpp + * @brief WebRTC interface implementation * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code @@ -38,7 +38,7 @@ namespace llwebrtc { -const float VOLUME_SCALE_WEBRTC = 3.0f; +const float VOLUME_SCALE_WEBRTC = 100; LLAudioDeviceObserver::LLAudioDeviceObserver() : mMicrophoneEnergy(0.0), mSumVector {0} {} @@ -57,7 +57,7 @@ void LLAudioDeviceObserver::OnCaptureData(const void *audio_samples, float sample = (static_cast(samples[index]) / (float) 32768); energy += sample * sample; } - + // smooth it. size_t buffer_size = sizeof(mSumVector) / sizeof(mSumVector[0]); float totalSum = 0; @@ -82,12 +82,12 @@ void LLAudioDeviceObserver::OnRenderData(const void *audio_samples, void LLWebRTCImpl::init() { + RTC_DCHECK(mPeerConnectionFactory); mPlayoutDevice = -1; mRecordingDevice = -1; - mAnswerReceived = false; rtc::InitializeSSL(); mTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory(); - + mNetworkThread = rtc::Thread::CreateWithSocketServer(); mNetworkThread->SetName("WebRTCNetworkThread", nullptr); mNetworkThread->Start(); @@ -97,61 +97,101 @@ void LLWebRTCImpl::init() mSignalingThread = rtc::Thread::Create(); mSignalingThread->SetName("WebRTCSignalingThread", nullptr); mSignalingThread->Start(); - + mWorkerThread->PostTask( - [this]() - { - mTuningAudioDeviceObserver = new LLAudioDeviceObserver; - mTuningDeviceModule = - webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, - mTaskQueueFactory.get(), - std::unique_ptr(mTuningAudioDeviceObserver)); - mTuningDeviceModule->Init(); - mTuningDeviceModule->SetStereoRecording(false); - mTuningDeviceModule->SetStereoPlayout(true); - mTuningDeviceModule->EnableBuiltInAEC(false); - updateDevices(); - }); + [this]() + { + mTuningAudioDeviceObserver = new LLAudioDeviceObserver; + mTuningDeviceModule = + webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, + mTaskQueueFactory.get(), + std::unique_ptr(mTuningAudioDeviceObserver)); + mTuningDeviceModule->Init(); + mTuningDeviceModule->SetStereoRecording(false); + mTuningDeviceModule->SetStereoPlayout(true); + mTuningDeviceModule->EnableBuiltInAEC(false); + updateDevices(); + }); + + rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); + webrtc::AudioProcessing::Config apm_config; + 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; + 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; + + mWorkerThread->BlockingCall( + [this]() + { + mPeerAudioDeviceObserver = new LLAudioDeviceObserver; + mPeerDeviceModule = + webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, + mTaskQueueFactory.get(), + std::unique_ptr(mPeerAudioDeviceObserver)); + mPeerDeviceModule->Init(); + mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); + mPeerDeviceModule->SetStereoRecording(false); + mPeerDeviceModule->SetStereoPlayout(true); + mPeerDeviceModule->EnableBuiltInAEC(false); + mPeerDeviceModule->InitMicrophone(); + mPeerDeviceModule->InitSpeaker(); + mPeerDeviceModule->InitRecording(); + mPeerDeviceModule->InitPlayout(); + mPeerDeviceModule->StartPlayout(); + mPeerDeviceModule->StartRecording(); + }); + + 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); + apm->ApplyConfig(apm_config); } void LLWebRTCImpl::terminate() { + for (auto& connection : mPeerConnections) + { + connection->terminate(); + } + mPeerConnections.clear(); + mSignalingThread->BlockingCall( - [this]() - { - if (mPeerConnection) - { - mPeerConnection->Close(); - mPeerConnection = nullptr; - } - mPeerConnectionFactory = nullptr; - }); + [this]() + { + mPeerConnectionFactory = nullptr; + }); mWorkerThread->BlockingCall( - [this]() - { - if (mTuningDeviceModule) - { - mTuningDeviceModule->StopRecording(); - mTuningDeviceModule->Terminate(); - } - if (mPeerDeviceModule) - { - mPeerDeviceModule->StopRecording(); - mPeerDeviceModule->Terminate(); - } - mTuningDeviceModule = nullptr; - mPeerDeviceModule = nullptr; - mTaskQueueFactory = nullptr; - }); - mNetworkThread->BlockingCall( - [this]() - { - if (mDataChannel) - { - mDataChannel->Close(); - mDataChannel = nullptr; - } - }); + [this]() + { + if (mTuningDeviceModule) + { + mTuningDeviceModule->StopRecording(); + mTuningDeviceModule->Terminate(); + } + if (mPeerDeviceModule) + { + mPeerDeviceModule->StopRecording(); + mPeerDeviceModule->Terminate(); + } + mTuningDeviceModule = nullptr; + mPeerDeviceModule = nullptr; + mTaskQueueFactory = nullptr; + }); } void LLWebRTCImpl::refreshDevices() @@ -164,7 +204,7 @@ void LLWebRTCImpl::setDevicesObserver(LLWebRTCDevicesObserver *observer) { mVoic void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer) { std::vector::iterator it = - std::find(mVoiceDevicesObserverList.begin(), mVoiceDevicesObserverList.end(), observer); + std::find(mVoiceDevicesObserverList.begin(), mVoiceDevicesObserverList.end(), observer); if (it != mVoiceDevicesObserverList.end()) { mVoiceDevicesObserverList.erase(it); @@ -174,97 +214,97 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer) void LLWebRTCImpl::setCaptureDevice(const std::string &id) { mWorkerThread->PostTask( - [this, id]() - { - 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; - mRecordingDevice = i; - break; - } - } - mTuningDeviceModule->StopRecording(); - mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); - mTuningDeviceModule->InitMicrophone(); - mTuningDeviceModule->InitRecording(); - mTuningDeviceModule->StartRecording(); - bool was_peer_recording = false; - if (mPeerDeviceModule) - { - was_peer_recording = mPeerDeviceModule->Recording(); - if (was_peer_recording) - { - mPeerDeviceModule->StopRecording(); - } - mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); - mPeerDeviceModule->InitMicrophone(); - mPeerDeviceModule->InitRecording(); - if (was_peer_recording) - { - mPeerDeviceModule->StartRecording(); - } - } - }); + [this, id]() + { + 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; + mRecordingDevice = i; + break; + } + } + mTuningDeviceModule->StopRecording(); + mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); + mTuningDeviceModule->InitMicrophone(); + mTuningDeviceModule->InitRecording(); + mTuningDeviceModule->StartRecording(); + bool was_peer_recording = false; + if (mPeerDeviceModule) + { + 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(); - 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 recording device to " << name << " " << guid << " " << i; - mPlayoutDevice = i; - break; - } - } - mTuningDeviceModule->SetSpeakerMute(true); - bool was_tuning_playing = mTuningDeviceModule->Playing(); - if (was_tuning_playing) - { - mTuningDeviceModule->StopPlayout(); - } - bool was_peer_mute = false; - if (mPeerDeviceModule) - { - mPeerDeviceModule->SpeakerMute(&was_peer_mute); - if (!was_peer_mute) - { - mPeerDeviceModule->SetSpeakerMute(true); - } - } - - mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); - mTuningDeviceModule->InitSpeaker(); - mTuningDeviceModule->InitPlayout(); - if (was_tuning_playing) - { - mTuningDeviceModule->StartPlayout(); - } - if (mPeerDeviceModule) - { - mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); - mPeerDeviceModule->InitSpeaker(); - mPeerDeviceModule->InitPlayout(); - mPeerDeviceModule->StartPlayout(); - mPeerDeviceModule->SetSpeakerMute(was_peer_mute); - - } - mTuningDeviceModule->SetSpeakerMute(false); - }); + [this, id]() + { + int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); + 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 recording device to " << name << " " << guid << " " << i; + mPlayoutDevice = i; + break; + } + } + mTuningDeviceModule->SetSpeakerMute(true); + bool was_tuning_playing = mTuningDeviceModule->Playing(); + if (was_tuning_playing) + { + mTuningDeviceModule->StopPlayout(); + } + bool was_peer_mute = false; + if (mPeerDeviceModule) + { + mPeerDeviceModule->SpeakerMute(&was_peer_mute); + if (!was_peer_mute) + { + mPeerDeviceModule->SetSpeakerMute(true); + } + } + + mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mTuningDeviceModule->InitSpeaker(); + mTuningDeviceModule->InitPlayout(); + if (was_tuning_playing) + { + mTuningDeviceModule->StartPlayout(); + } + if (mPeerDeviceModule) + { + mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mPeerDeviceModule->InitSpeaker(); + mPeerDeviceModule->InitPlayout(); + mPeerDeviceModule->StartPlayout(); + mPeerDeviceModule->SetSpeakerMute(was_peer_mute); + + } + mTuningDeviceModule->SetSpeakerMute(false); + }); } void LLWebRTCImpl::updateDevices() @@ -278,7 +318,7 @@ void LLWebRTCImpl::updateDevices() mTuningDeviceModule->PlayoutDeviceName(index, name, guid); renderDeviceList.emplace_back(name, guid); } - + int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); LLWebRTCVoiceDeviceList captureDeviceList; for (int16_t index = 0; index < captureDeviceCount; index++) @@ -297,113 +337,100 @@ void LLWebRTCImpl::updateDevices() void LLWebRTCImpl::setTuningMode(bool enable) { mWorkerThread->BlockingCall( - [this, enable]() - { - if (enable) - { + [this, enable]() + { + if (enable) + { + + mTuningDeviceModule->StartRecording(); + mTuningDeviceModule->SetMicrophoneMute(false); + + + mTuningDeviceModule->SetSpeakerMute(false); + + if (mPeerDeviceModule) + { + mPeerDeviceModule->StopRecording(); + mPeerDeviceModule->SetSpeakerMute(true); + } + } + else + { + if (mPeerDeviceModule) + { + mPeerDeviceModule->StartRecording(); + mPeerDeviceModule->SetSpeakerMute(false); + } + } + }); + for (auto& connection : mPeerConnections) + { + connection->enableTracks(enable ? false : !mMute); + } +} - mTuningDeviceModule->StartRecording(); - mTuningDeviceModule->SetMicrophoneMute(false); +float LLWebRTCImpl::getTuningAudioLevel() { return 20 * mTuningAudioDeviceObserver->getMicrophoneEnergy(); } +float LLWebRTCImpl::getPeerAudioLevel() { return 20 * mPeerAudioDeviceObserver->getMicrophoneEnergy(); } - mTuningDeviceModule->SetSpeakerMute(false); +void LLWebRTCImpl::setSpeakerVolume(float volume) { mPeerDeviceModule->SetSpeakerVolume(volume * VOLUME_SCALE_WEBRTC);} +void LLWebRTCImpl::setMicrophoneVolume(float volume) { mPeerDeviceModule->SetMicrophoneVolume(volume * VOLUME_SCALE_WEBRTC);} - if (mPeerDeviceModule) - { - mPeerDeviceModule->StopRecording(); - mPeerDeviceModule->SetSpeakerMute(true); - } - } - else - { - if (mPeerDeviceModule) - { - mPeerDeviceModule->StartRecording(); - mPeerDeviceModule->SetSpeakerMute(false); - } - } - }); - // set_enabled shouldn't be done on the worker thread - if (mPeerConnection) +// +// Helpers +// + +LLWebRTCPeerConnection * LLWebRTCImpl::newPeerConnection() +{ + rtc::scoped_refptr peerConnection = rtc::scoped_refptr(new rtc::RefCountedObject()); + peerConnection->init(this); + + mPeerConnections.emplace_back(peerConnection); + peerConnection->enableTracks(!mMute); + return peerConnection.get(); +} +void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnection * peer_connection) +{ + std::vector>::iterator it = + std::find(mPeerConnections.begin(), mPeerConnections.end(), peer_connection); + if (it != mPeerConnections.end()) { - auto senders = mPeerConnection->GetSenders(); - for (auto &sender : senders) - { - sender->track()->set_enabled(enable ? false : !mMute); - } + (*it)->terminate(); + mPeerConnections.erase(it); } } // -// LLWebRTCSignalInterface +// LLWebRTCPeerConnection interface // -void LLWebRTCImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); } +void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl) +{ + mWebRTCImpl = webrtc_impl; + mPeerConnectionFactory = mWebRTCImpl->getPeerConnectionFactory(); +} +void LLWebRTCPeerConnectionImpl::terminate() +{ + shutdownConnection(); +} + +void LLWebRTCPeerConnectionImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); } -void LLWebRTCImpl::unsetSignalingObserver(LLWebRTCSignalingObserver *observer) +void LLWebRTCPeerConnectionImpl::unsetSignalingObserver(LLWebRTCSignalingObserver *observer) { std::vector::iterator it = - std::find(mSignalingObserverList.begin(), mSignalingObserverList.end(), observer); + std::find(mSignalingObserverList.begin(), mSignalingObserverList.end(), observer); if (it != mSignalingObserverList.end()) { mSignalingObserverList.erase(it); } } -bool LLWebRTCImpl::initializeConnection() +bool LLWebRTCPeerConnectionImpl::initializeConnection() { RTC_DCHECK(!mPeerConnection); - RTC_DCHECK(mPeerConnectionFactory); mAnswerReceived = false; - rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); - webrtc::AudioProcessing::Config apm_config; - 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; - 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; - - mWorkerThread->BlockingCall( - [this]() - { - mPeerAudioDeviceObserver = new LLAudioDeviceObserver; - mPeerDeviceModule = - webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, - mTaskQueueFactory.get(), - std::unique_ptr(mPeerAudioDeviceObserver)); - mPeerDeviceModule->Init(); - mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); - mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); - mPeerDeviceModule->SetStereoRecording(false); - mPeerDeviceModule->SetStereoPlayout(true); - mPeerDeviceModule->EnableBuiltInAEC(false); - mPeerDeviceModule->InitMicrophone(); - mPeerDeviceModule->InitSpeaker(); - mPeerDeviceModule->InitRecording(); - mPeerDeviceModule->InitPlayout(); - mPeerDeviceModule->StopPlayout(); - mPeerDeviceModule->StopRecording(); - }); - - 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); - apm->ApplyConfig(apm_config); - webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer server; @@ -419,7 +446,7 @@ bool LLWebRTCImpl::initializeConnection() config.servers.push_back(server); server.uri = "stun:stun4.l.google.com:19302"; config.servers.push_back(server); - + webrtc::PeerConnectionDependencies pc_dependencies(this); auto error_or_peer_connection = mPeerConnectionFactory->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); if (error_or_peer_connection.ok()) @@ -432,35 +459,36 @@ bool LLWebRTCImpl::initializeConnection() shutdownConnection(); return false; } - + 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); } - + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); - + cricket::AudioOptions audioOptions; audioOptions.auto_gain_control = true; audioOptions.echo_cancellation = false; // incompatible with opus stereo audioOptions.noise_suppression = true; - + rtc::scoped_refptr stream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); + rtc::scoped_refptr audio_track( - mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(audioOptions).get())); + mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(audioOptions).get())); audio_track->set_enabled(true); stream->AddTrack(audio_track); - + mPeerConnection->AddTrack(audio_track, {"SLStream"}); - + auto senders = mPeerConnection->GetSenders(); - + for (auto &sender : senders) { webrtc::RtpParameters params; @@ -474,7 +502,7 @@ bool LLWebRTCImpl::initializeConnection() params.codecs.push_back(codecparam); sender->SetParameters(params); } - + auto receivers = mPeerConnection->GetReceivers(); for (auto &receiver : receivers) { @@ -491,102 +519,78 @@ bool LLWebRTCImpl::initializeConnection() } webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions; mPeerConnection->CreateOffer(this, offerOptions); - + return true; } -void LLWebRTCImpl::shutdownConnection() +void LLWebRTCPeerConnectionImpl::shutdownConnection() { - mSignalingThread->BlockingCall( - [this]() - { - if (mPeerConnection) - { - mPeerConnection->Close(); - mPeerConnection = nullptr; - } - mPeerConnectionFactory = nullptr; - }); - mWorkerThread->BlockingCall( - [this]() - { - if (mPeerDeviceModule) - { - mPeerDeviceModule->StopRecording(); - mPeerDeviceModule->Terminate(); - } - mPeerDeviceModule = nullptr; - if (mPeerAudioDeviceObserver) - { - mPeerAudioDeviceObserver = nullptr; - } - }); - mNetworkThread->BlockingCall( - [this]() + mWebRTCImpl->SignalingBlockingCall( + [this]() + { + if (mPeerConnection) + { + mPeerConnection->Close(); + mPeerConnection = nullptr; + } + }); + + mWebRTCImpl->NetworkBlockingCall( + [this]() + { + if (mDataChannel) + { + mDataChannel->Close(); + mDataChannel = nullptr; + } + }); +} + +void LLWebRTCPeerConnectionImpl::enableTracks(bool enable) +{ + // set_enabled shouldn't be done on the worker thread + if (mPeerConnection) + { + auto senders = mPeerConnection->GetSenders(); + for (auto &sender : senders) { - if (mDataChannel) - { - mDataChannel->Close(); - mDataChannel = nullptr; - } - }); + sender->track()->set_enabled(enable); + } + } } -void LLWebRTCImpl::AnswerAvailable(const std::string &sdp) +void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp) { RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp; - - mSignalingThread->PostTask( - [this, sdp]() - { - RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); - mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), - rtc::scoped_refptr(this)); - }); + + mWebRTCImpl->PostSignalingTask( + [this, sdp]() + { + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); + mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), + rtc::scoped_refptr(this)); + }); } -void LLWebRTCImpl::setMute(bool mute) +void LLWebRTCPeerConnectionImpl::setMute(bool mute) { mMute = mute; auto senders = mPeerConnection->GetSenders(); - + RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); - + for (auto &sender : senders) { sender->track()->set_enabled(!mMute); } } -void LLWebRTCImpl::setSpeakerVolume(float volume) -{ - mSignalingThread->PostTask( - [this, volume]() - { - auto receivers = mPeerConnection->GetReceivers(); - - RTC_LOG(LS_INFO) << __FUNCTION__ << "Set volume" << receivers.size(); - for (auto &receiver : receivers) - { - webrtc::MediaStreamTrackInterface *track = receiver->track().get(); - if (track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) - { - webrtc::AudioTrackInterface *audio_track = static_cast(track); - webrtc::AudioSourceInterface *source = audio_track->GetSource(); - source->SetVolume(VOLUME_SCALE_WEBRTC * volume); - } - } - }); -} - -float LLWebRTCImpl::getTuningAudioLevel() { return 20 * mTuningAudioDeviceObserver->getMicrophoneEnergy(); } - // // PeerConnectionObserver implementation. // -void LLWebRTCImpl::OnAddTrack(rtc::scoped_refptr receiver, - const std::vector> &streams) +void LLWebRTCPeerConnectionImpl::OnAddTrack(rtc::scoped_refptr receiver, + const std::vector> &streams) { RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); webrtc::RtpParameters params; @@ -601,18 +605,18 @@ void LLWebRTCImpl::OnAddTrack(rtc::scoped_refptr receiver->SetParameters(params); } -void LLWebRTCImpl::OnRemoveTrack(rtc::scoped_refptr receiver) +void LLWebRTCPeerConnectionImpl::OnRemoveTrack(rtc::scoped_refptr receiver) { RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); } -void LLWebRTCImpl::OnDataChannel(rtc::scoped_refptr channel) +void LLWebRTCPeerConnectionImpl::OnDataChannel(rtc::scoped_refptr channel) { mDataChannel = channel; channel->RegisterObserver(this); } -void LLWebRTCImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) +void LLWebRTCPeerConnectionImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) { LLWebRTCSignalingObserver::IceGatheringState webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; switch (new_state) @@ -631,7 +635,7 @@ void LLWebRTCImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGath webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; return; } - + if (mAnswerReceived) { for (auto &observer : mSignalingObserverList) @@ -642,19 +646,17 @@ void LLWebRTCImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGath } // Called any time the PeerConnectionState changes. -void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState new_state) +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: { if (new_state == webrtc::PeerConnectionInterface::PeerConnectionState::kConnected) { - mWorkerThread->PostTask([this]() { - mPeerDeviceModule->StartRecording(); - mPeerDeviceModule->StartPlayout(); + mWebRTCImpl->PostWorkerTask([this]() { for (auto &observer : mSignalingObserverList) { observer->OnAudioEstablished(this); @@ -670,7 +672,7 @@ void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConne { observer->OnRenegotiationNeeded(); } - + break; } default: @@ -683,15 +685,15 @@ void LLWebRTCImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConne 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 "; - + 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"; @@ -699,25 +701,25 @@ static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterfa 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(); + "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(); + "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(); + "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") + if (candidate->candidate().protocol() == "tcp") { candidate_stream << " tcptype " << candidate->candidate().tcptype(); } @@ -725,16 +727,16 @@ static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterfa return candidate_stream.str(); } -void LLWebRTCImpl::OnIceCandidate(const webrtc::IceCandidateInterface *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) + if (mAnswerReceived) { for (auto &observer : mSignalingObserverList) { @@ -745,22 +747,22 @@ void LLWebRTCImpl::OnIceCandidate(const webrtc::IceCandidateInterface *candidate observer->OnIceCandidate(ice_candidate); } } - else + else { mCachedIceCandidates.push_back( - webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); + webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); } } // // CreateSessionDescriptionObserver implementation. // -void LLWebRTCImpl::OnSuccess(webrtc::SessionDescriptionInterface *desc) +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); @@ -780,7 +782,7 @@ void LLWebRTCImpl::OnSuccess(webrtc::SessionDescriptionInterface *desc) 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\n"; + << " minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1;maxplaybackrate=48000\n"; } else { @@ -789,27 +791,27 @@ void LLWebRTCImpl::OnSuccess(webrtc::SessionDescriptionInterface *desc) } webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str()); - - - + + + mPeerConnection->SetLocalDescription(std::unique_ptr( - webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str())), - rtc::scoped_refptr(this)); + webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str())), + rtc::scoped_refptr(this)); RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_mangled_stream.str(); - - + + for (auto &observer : mSignalingObserverList) { observer->OnOfferAvailable(sdp_mangled_stream.str()); } } -void LLWebRTCImpl::OnFailure(webrtc::RTCError error) { RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); } +void LLWebRTCPeerConnectionImpl::OnFailure(webrtc::RTCError error) { RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); } // // SetRemoteDescriptionObserverInterface implementation. // -void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) +void LLWebRTCPeerConnectionImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) { RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); if (!error.ok()) @@ -831,37 +833,20 @@ void LLWebRTCImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error) } mCachedIceCandidates.clear(); OnIceGatheringChange(mPeerConnection->ice_gathering_state()); - + } // // SetLocalDescriptionObserverInterface implementation. // -void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) +void LLWebRTCPeerConnectionImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) { -#if 0 - RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); - if (!error.ok()) - { - RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); - return; - } - auto desc = mPeerConnection->pending_local_description(); - std::string sdp; - desc->ToString(&sdp); - - RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp; - ; - for (auto &observer : mSignalingObserverList) - { - observer->OnOfferAvailable(sdp); - } -#endif + } -void LLWebRTCImpl::setAudioObserver(LLWebRTCAudioObserver *observer) { mAudioObserverList.emplace_back(observer); } +void LLWebRTCPeerConnectionImpl::setAudioObserver(LLWebRTCAudioObserver *observer) { mAudioObserverList.emplace_back(observer); } -void LLWebRTCImpl::unsetAudioObserver(LLWebRTCAudioObserver *observer) +void LLWebRTCPeerConnectionImpl::unsetAudioObserver(LLWebRTCAudioObserver *observer) { std::vector::iterator it = std::find(mAudioObserverList.begin(), mAudioObserverList.end(), observer); if (it != mAudioObserverList.end()) @@ -870,22 +855,20 @@ void LLWebRTCImpl::unsetAudioObserver(LLWebRTCAudioObserver *observer) } } -float LLWebRTCImpl::getAudioLevel() { return 20 * mPeerAudioDeviceObserver->getMicrophoneEnergy(); } - // // DataChannelObserver implementation // -void LLWebRTCImpl::OnStateChange() -{ +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 : mDataObserverList) + for (auto &observer : mSignalingObserverList) { - observer->OnDataChannelReady(); + observer->OnDataChannelReady(this); } break; case webrtc::DataChannelInterface::kConnecting: @@ -903,7 +886,7 @@ void LLWebRTCImpl::OnStateChange() } -void LLWebRTCImpl::OnMessage(const webrtc::DataBuffer& buffer) +void LLWebRTCPeerConnectionImpl::OnMessage(const webrtc::DataBuffer& buffer) { std::string data((const char*)buffer.data.cdata(), buffer.size()); for (auto &observer : mDataObserverList) @@ -912,43 +895,55 @@ void LLWebRTCImpl::OnMessage(const webrtc::DataBuffer& buffer) } } -void LLWebRTCImpl::sendData(const std::string& data, bool binary) +void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary) { rtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length()); webrtc::DataBuffer buffer(cowBuffer, binary); mDataChannel->Send(buffer); } -void LLWebRTCImpl::setDataObserver(LLWebRTCDataObserver* observer) { mDataObserverList.emplace_back(observer); } +void LLWebRTCPeerConnectionImpl::setDataObserver(LLWebRTCDataObserver* observer) { mDataObserverList.emplace_back(observer); } -void LLWebRTCImpl::unsetDataObserver(LLWebRTCDataObserver* observer) +void LLWebRTCPeerConnectionImpl::unsetDataObserver(LLWebRTCDataObserver* observer) { std::vector::iterator it = - std::find(mDataObserverList.begin(), mDataObserverList.end(), observer); + std::find(mDataObserverList.begin(), mDataObserverList.end(), observer); if (it != mDataObserverList.end()) { mDataObserverList.erase(it); } } -rtc::RefCountedObject *gWebRTCImpl = nullptr; -LLWebRTCDeviceInterface *getDeviceInterface() { return gWebRTCImpl; } -LLWebRTCSignalInterface *getSignalingInterface() { return gWebRTCImpl; } -LLWebRTCDataInterface *getDataInterface() { return gWebRTCImpl; } +LLWebRTCImpl * gWebRTCImpl = nullptr; +LLWebRTCDeviceInterface * getDeviceInterface() +{ + return gWebRTCImpl; +} + +LLWebRTCPeerConnection * newPeerConnection() +{ + return gWebRTCImpl->newPeerConnection(); +} + +void freePeerConnection(LLWebRTCPeerConnection *peer_connection) +{ + gWebRTCImpl->freePeerConnection(peer_connection); +} void init() { - gWebRTCImpl = new rtc::RefCountedObject(); - gWebRTCImpl->AddRef(); + gWebRTCImpl = new LLWebRTCImpl(); gWebRTCImpl->init(); } void terminate() -{ - gWebRTCImpl->terminate(); - gWebRTCImpl->Release(); - gWebRTCImpl = nullptr; +{ + if (gWebRTCImpl) + { + gWebRTCImpl->terminate(); + gWebRTCImpl = nullptr; + } } } // namespace llwebrtc diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 9224e88dfa..753fe6a983 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -1,6 +1,6 @@ /** - * @file llaccordionctrl.cpp - * @brief Accordion panel implementation + * @file llwebrtc.h + * @brief WebRTC interface * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code @@ -17,7 +17,7 @@ * 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 + * 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 @@ -44,8 +44,6 @@ namespace llwebrtc { -LLSYMEXPORT void init(); -LLSYMEXPORT void terminate(); struct LLWebRTCIceCandidate { @@ -87,6 +85,10 @@ class LLWebRTCDeviceInterface virtual void setTuningMode(bool enable) = 0; virtual float getTuningAudioLevel() = 0; + + virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 + virtual void setMicrophoneVolume(float volume) = 0; // volume between 0.0 and 1.0 + virtual float getPeerAudioLevel() = 0; }; class LLWebRTCAudioObserver @@ -100,15 +102,12 @@ class LLWebRTCAudioInterface virtual void setAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void unsetAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void setMute(bool mute) = 0; - virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 - virtual float getAudioLevel() = 0; }; class LLWebRTCDataObserver { public: virtual void OnDataReceived(const std::string& data, bool binary) = 0; - virtual void OnDataChannelReady() = 0; }; class LLWebRTCDataInterface @@ -133,9 +132,10 @@ class LLWebRTCSignalingObserver virtual void OnOfferAvailable(const std::string& sdp) = 0; virtual void OnRenegotiationNeeded() = 0; virtual void OnAudioEstablished(LLWebRTCAudioInterface *audio_interface) = 0; + virtual void OnDataChannelReady(LLWebRTCDataInterface *data_interface) = 0; }; -class LLWebRTCSignalInterface +class LLWebRTCPeerConnection { public: virtual void setSignalingObserver(LLWebRTCSignalingObserver* observer) = 0; @@ -146,9 +146,11 @@ class LLWebRTCSignalInterface virtual void AnswerAvailable(const std::string &sdp) = 0; }; +LLSYMEXPORT void init(); +LLSYMEXPORT void terminate(); LLSYMEXPORT LLWebRTCDeviceInterface* getDeviceInterface(); -LLSYMEXPORT LLWebRTCSignalInterface* getSignalingInterface(); -LLSYMEXPORT LLWebRTCDataInterface* getDataInterface(); +LLSYMEXPORT LLWebRTCPeerConnection* newPeerConnection(); +LLSYMEXPORT void freePeerConnection(LLWebRTCPeerConnection *connection); } #endif // LLWEBRTC_H diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 01159604c9..295e949b4e 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -1,6 +1,6 @@ /** - * @file llaccordionctrl.cpp - * @brief Accordion panel implementation + * @file llwebrtc_impl.h + * @brief WebRTC Interface implementation. * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code @@ -63,6 +63,8 @@ namespace llwebrtc { +class LLWebRTCPeerConnectionImpl; + class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver { public: @@ -87,16 +89,7 @@ class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver float mMicrophoneEnergy; }; -class LLWebRTCImpl : public LLWebRTCDeviceInterface, - public LLWebRTCSignalInterface, - public LLWebRTCAudioInterface, - public LLWebRTCDataInterface, - public webrtc::PeerConnectionObserver, - public webrtc::CreateSessionDescriptionObserver, - public webrtc::SetRemoteDescriptionObserverInterface, - public webrtc::SetLocalDescriptionObserverInterface, - public webrtc::DataChannelObserver - +class LLWebRTCImpl : public LLWebRTCDeviceInterface { public: LLWebRTCImpl() : @@ -116,15 +109,112 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void setDevicesObserver(LLWebRTCDevicesObserver *observer) override; void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) override; - void setCaptureDevice(const std::string& id) override; + void setCaptureDevice(const std::string& id) override; void setRenderDevice(const std::string& id) override; void setTuningMode(bool enable) override; float getTuningAudioLevel() override; + float getPeerAudioLevel() override; + + void setSpeakerVolume(float volume) override; // range 0.0-1.0 + void setMicrophoneVolume(float volume) override; // range 0.0-1.0 + + // + // Helpers + // + + void PostWorkerTask(absl::AnyInvocable task, + const webrtc::Location& location = webrtc::Location::Current()) + { + mWorkerThread->PostTask(std::move(task), location); + } + + void PostSignalingTask(absl::AnyInvocable task, + const webrtc::Location& location = webrtc::Location::Current()) + { + mSignalingThread->PostTask(std::move(task), location); + } + + void PostNetworkTask(absl::AnyInvocable task, + const webrtc::Location& location = webrtc::Location::Current()) + { + mNetworkThread->PostTask(std::move(task), location); + } + + void WorkerBlockingCall(rtc::FunctionView functor, + const webrtc::Location& location = webrtc::Location::Current()) + { + mWorkerThread->BlockingCall(std::move(functor), location); + } + + void SignalingBlockingCall(rtc::FunctionView functor, + const webrtc::Location& location = webrtc::Location::Current()) + { + mSignalingThread->BlockingCall(std::move(functor), location); + } + + void NetworkBlockingCall(rtc::FunctionView functor, + const webrtc::Location& location = webrtc::Location::Current()) + { + mNetworkThread->BlockingCall(std::move(functor), location); + } + + rtc::scoped_refptr getPeerConnectionFactory() { return mPeerConnectionFactory; } + + LLWebRTCPeerConnection * newPeerConnection(); + void freePeerConnection(LLWebRTCPeerConnection * peer_connection); + + protected: + + std::unique_ptr mNetworkThread; + std::unique_ptr mWorkerThread; + std::unique_ptr mSignalingThread; + rtc::scoped_refptr mPeerConnectionFactory; + webrtc::PeerConnectionInterface::RTCConfiguration mConfiguration; + std::unique_ptr mTaskQueueFactory; + + + // Devices + void updateDevices(); + rtc::scoped_refptr mTuningDeviceModule; + rtc::scoped_refptr mPeerDeviceModule; + std::vector mVoiceDevicesObserverList; + // accessors in webrtc aren't apparently implemented yet. + int32_t mPlayoutDevice; + int32_t mRecordingDevice; + bool mMute; + + LLAudioDeviceObserver * mTuningAudioDeviceObserver; + LLAudioDeviceObserver * mPeerAudioDeviceObserver; + + // peer connections + std::vector> mPeerConnections; +}; + +class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, + 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; + // - // LLWebRTCSignalInterface + // LLWebRTCPeerConnection // void setSignalingObserver(LLWebRTCSignalingObserver *observer) override; @@ -140,8 +230,6 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, void setAudioObserver(LLWebRTCAudioObserver *observer) override; void unsetAudioObserver(LLWebRTCAudioObserver *observer) override; void setMute(bool mute) override; - void setSpeakerVolume(float folume) override; // range 0.0-1.0 - float getAudioLevel() override; // // LLWebRTCDataInterface @@ -187,31 +275,17 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, // void OnStateChange() override; void OnMessage(const webrtc::DataBuffer& buffer) override; + + // Helpers + void enableTracks(bool enable); protected: - - std::unique_ptr mNetworkThread; - std::unique_ptr mWorkerThread; - std::unique_ptr mSignalingThread; + + LLWebRTCImpl * mWebRTCImpl; rtc::scoped_refptr mPeerConnectionFactory; - webrtc::PeerConnectionInterface::RTCConfiguration mConfiguration; - std::unique_ptr mTaskQueueFactory; - - - // Devices - void updateDevices(); - rtc::scoped_refptr mTuningDeviceModule; - rtc::scoped_refptr mPeerDeviceModule; - std::vector mVoiceDevicesObserverList; - // accessors in webrtc aren't apparently implemented yet. - int32_t mPlayoutDevice; - int32_t mRecordingDevice; bool mMute; - LLAudioDeviceObserver * mTuningAudioDeviceObserver; - LLAudioDeviceObserver * mPeerAudioDeviceObserver; - // signaling std::vector mSignalingObserverList; std::vector> mCachedIceCandidates; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b4d65bec0a..4fe2c78167 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -307,7 +307,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mIsCoroutineActive(false), mWebRTCPump("WebRTCClientPump"), mWebRTCDeviceInterface(nullptr), - mWebRTCSignalingInterface(nullptr), + mWebRTCPeerConnection(nullptr), mWebRTCAudioInterface(nullptr) { sShuttingDown = false; @@ -364,11 +364,8 @@ void LLWebRTCVoiceClient::init(LLPumpIO *pump) mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); mWebRTCDeviceInterface->setDevicesObserver(this); - mWebRTCSignalingInterface = llwebrtc::getSignalingInterface(); - mWebRTCSignalingInterface->setSignalingObserver(this); - - mWebRTCDataInterface = llwebrtc::getDataInterface(); - mWebRTCDataInterface->setDataObserver(this); + mWebRTCPeerConnection = llwebrtc::newPeerConnection(); + mWebRTCPeerConnection->setSignalingObserver(this); } void LLWebRTCVoiceClient::terminate() @@ -500,7 +497,7 @@ void LLWebRTCVoiceClient::setLoginInfo( const std::string& channel_sdp) { mRemoteChannelSDP = channel_sdp; - mWebRTCSignalingInterface->AnswerAvailable(channel_sdp); + mWebRTCPeerConnection->AnswerAvailable(channel_sdp); if(mAccountLoggedIn) { @@ -862,7 +859,7 @@ bool LLWebRTCVoiceClient::establishVoiceConnection() { return false; } - return mWebRTCSignalingInterface->initializeConnection(); + return mWebRTCPeerConnection->initializeConnection(); } bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) @@ -896,7 +893,7 @@ bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) LLSD body; body["logout"] = TRUE; httpAdapter->postAndSuspend(httpRequest, url, body); - mWebRTCSignalingInterface->shutdownConnection(); + mWebRTCPeerConnection->shutdownConnection(); return true; } @@ -1136,11 +1133,14 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession addParticipantByID(gAgent.getID()); // tell peers that this participant has joined. - Json::FastWriter writer; - Json::Value root = getPositionAndVolumeUpdateJson(true); - root["j"] = true; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + if (mWebRTCDataInterface) + { + Json::FastWriter writer; + Json::Value root = getPositionAndVolumeUpdateJson(true); + root["j"] = true; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); + } notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); @@ -2235,7 +2235,7 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) if (!mMuteMic) { - audio_level = (F32) mWebRTCAudioInterface->getAudioLevel(); + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); } uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); if (force || (uint_audio_level != mAudioLevel)) @@ -2451,7 +2451,6 @@ void LLWebRTCVoiceClient::processIceUpdates() if (mIceCandidates.size()) { LLSD candidates = LLSD::emptyArray(); - body["candidates"] = LLSD::emptyArray(); for (auto &ice_candidate : mIceCandidates) { LLSD body_candidate; @@ -2532,7 +2531,7 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * LLMutexLock lock(&mVoiceStateMutex); speaker_volume = mSpeakerVolume; } - audio_interface->setSpeakerVolume(mSpeakerVolume); + mWebRTCDeviceInterface->setSpeakerVolume(mSpeakerVolume); setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } @@ -2597,9 +2596,10 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) } } -void LLWebRTCVoiceClient::OnDataChannelReady() +void LLWebRTCVoiceClient::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { - + mWebRTCDataInterface = data_interface; + mWebRTCDataInterface->setDataObserver(this); } @@ -4470,7 +4470,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) } if (mWebRTCAudioInterface) { - mWebRTCAudioInterface->setSpeakerVolume(volume); + mWebRTCDeviceInterface->setSpeakerVolume(volume); } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index fd7094cb46..b33dc26dff 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -271,7 +271,7 @@ public: /// LLWebRTCDataObserver //@{ void OnDataReceived(const std::string& data, bool binary) override; - void OnDataChannelReady() override; + void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; //@} void processIceUpdates(); @@ -778,7 +778,7 @@ private: buddyListMap mBuddyListMap; llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; - llwebrtc::LLWebRTCSignalInterface *mWebRTCSignalingInterface; + llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; -- cgit v1.2.3 From 1b77987a0b84fac6a0d07b7782a23bb22aa48ec6 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 8 Nov 2023 10:17:56 -0800 Subject: comment fixes --- indra/llwebrtc/llwebrtc.cpp | 2 +- indra/llwebrtc/llwebrtc_impl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 6eb3478b59..62c42ead1b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -1,6 +1,6 @@ /** * @file llwebrtc.cpp - * @brief WebRTC interface implementation + * @brief WebRTC interface implementation * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 295e949b4e..eb439b5a46 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -1,6 +1,6 @@ /** * @file llwebrtc_impl.h - * @brief WebRTC Interface implementation. + * @brief WebRTC interface implementation header * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code -- cgit v1.2.3 From c33ebcfab6e4fda3f7858f1fa0f65d8c4e82f714 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 9 Nov 2023 18:19:18 -0800 Subject: Remove a bunch of unnecessary code. --- indra/llwebrtc/llwebrtc.cpp | 4 +- indra/newview/llvoicewebrtc.cpp | 2480 ++------------------------------------- indra/newview/llvoicewebrtc.h | 252 +--- 3 files changed, 123 insertions(+), 2613 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 62c42ead1b..bf1e04c2f2 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -373,8 +373,8 @@ float LLWebRTCImpl::getTuningAudioLevel() { return 20 * mTuningAudioDeviceObserv float LLWebRTCImpl::getPeerAudioLevel() { return 20 * mPeerAudioDeviceObserver->getMicrophoneEnergy(); } -void LLWebRTCImpl::setSpeakerVolume(float volume) { mPeerDeviceModule->SetSpeakerVolume(volume * VOLUME_SCALE_WEBRTC);} -void LLWebRTCImpl::setMicrophoneVolume(float volume) { mPeerDeviceModule->SetMicrophoneVolume(volume * VOLUME_SCALE_WEBRTC);} +void LLWebRTCImpl::setSpeakerVolume(float volume) { mPeerDeviceModule->SetSpeakerVolume( (uint32_t)(volume * VOLUME_SCALE_WEBRTC));} +void LLWebRTCImpl::setMicrophoneVolume(float volume) { mPeerDeviceModule->SetMicrophoneVolume((uint32_t)(volume * VOLUME_SCALE_WEBRTC));} // // Helpers diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 4fe2c78167..93efd2526d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -131,12 +131,6 @@ static int scale_mic_volume(float volume) /////////////////////////////////////////////////////////////////////////////////////////////// -class LLWebRTCVoiceClientMuteListObserver : public LLMuteListObserver -{ - /* virtual */ void onChange() { LLWebRTCVoiceClient::getInstance()->muteListChanged();} -}; - - void LLVoiceWebRTCStats::reset() { mStartTime = -1.0f; @@ -227,10 +221,6 @@ LLSD LLVoiceWebRTCStats::read() return stats; } -static LLWebRTCVoiceClientMuteListObserver mutelist_listener; -static bool sMuteListListener_listening = false; - - /////////////////////////////////////////////////////////////////////////////////////////////// bool LLWebRTCVoiceClient::sShuttingDown = false; @@ -285,14 +275,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mLipSyncEnabled(false), - mVoiceFontsReceived(false), - mVoiceFontsNew(false), - mVoiceFontListDirty(false), - - mCaptureBufferMode(false), - mCaptureBufferRecording(false), - mCaptureBufferRecorded(false), - mCaptureBufferPlaying(false), mShutdownComplete(true), mPlayRequestCount(0), @@ -390,8 +372,6 @@ void LLWebRTCVoiceClient::cleanUp() LL_DEBUGS("Voice") << LL_ENDL; deleteAllSessions(); - deleteAllVoiceFonts(); - deleteVoiceFontTemplates(); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -418,55 +398,6 @@ void LLWebRTCVoiceClient::updateSettings() setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); } -///////////////////////////// -// utility functions - -bool LLWebRTCVoiceClient::writeString(const std::string &str) -{ - bool result = false; - LL_DEBUGS("LowVoice") << "sending:\n" << str << LL_ENDL; - - if(sConnected) - { - 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; - - // check return code - sockets will fail (broken, etc.) - err = apr_socket_send( - mSocket->getSocket(), - (const char*)str.data(), - &written); - - if(err == 0 && written == size) - { - // Success. - result = true; - } - else if (err == 0 && written != size) { - // Did a short write, log it for now - LL_WARNS("Voice") << ") short write on socket sending data to WebRTC daemon." << "Sent " << written << "bytes instead of " << size <getRegionID().asString()); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -903,13 +837,6 @@ bool LLWebRTCVoiceClient::loginToWebRTC() mIsLoggedIn = true; notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - // Set up the mute list observer if it hasn't been set up already. - if ((!sMuteListListener_listening)) - { - LLMuteList::getInstance()->addObserver(&mutelist_listener); - sMuteListListener_listening = true; - } - // Set the initial state of mic mute, local speaker volume, etc. sendLocalAudioUpdates(); mIsLoggingIn = false; @@ -998,6 +925,8 @@ bool LLWebRTCVoiceClient::requestParcelVoiceInfo() std::string uri; std::string credentials; + LL_WARNS("Voice") << "Got voice credentials" << result << LL_ENDL; + if (result.has("voice_credentials")) { LLSD voice_credentials = result["voice_credentials"]; @@ -1050,7 +979,7 @@ bool LLWebRTCVoiceClient::requestParcelVoiceInfo() // 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(uri, ""); } bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) @@ -1158,24 +1087,6 @@ bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) { if (!mAudioSession->mHandle.empty()) { - -#if RECORD_EVERYTHING - // HACK: for testing only - // Save looped recording - std::string savepath("/tmp/WebRTCrecording"); - { - 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; - } - recordingLoopSave(savepath); -#endif - - sessionMediaDisconnectSendMessage(mAudioSession); - if (wait) { LLSD result; @@ -1304,10 +1215,6 @@ bool LLWebRTCVoiceClient::waitForChannel() { performMicTuning(); } - else if (mCaptureBufferMode) - { - recordingAndPlaybackMode(); - } else if (checkParcelChanged() || (mNextAudioSession == NULL)) { // the parcel is changed, or we have no pending audio sessions, @@ -1399,7 +1306,6 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) } notifyParticipantObservers(); - notifyVoiceFontObservers(); LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); @@ -1465,13 +1371,6 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) } sendPositionAndVolumeUpdate(); - // Do notifications for expiring Voice Fonts. - if (mVoiceFontExpiryTimer.hasExpired()) - { - expireVoiceFonts(); - mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); - } - // send any requests to adjust mic and speaker settings if they have changed sendLocalAudioUpdates(); @@ -1537,112 +1436,6 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) return true; } -void LLWebRTCVoiceClient::recordingAndPlaybackMode() -{ - LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL; - - while (true) - { - LLSD command; - do - { - command = llcoro::suspendUntilEventOn(mWebRTCPump); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(command) << LL_ENDL; - } while (!command.has("recplay")); - - if (command["recplay"].asString() == "quit") - { - mCaptureBufferMode = false; - break; - } - else if (command["recplay"].asString() == "record") - { - voiceRecordBuffer(); - } - else if (command["recplay"].asString() == "playback") - { - voicePlaybackBuffer(); - } - } - - LL_INFOS("Voice") << "Leaving capture/playback mode." << LL_ENDL; - mCaptureBufferRecording = false; - mCaptureBufferRecorded = false; - mCaptureBufferPlaying = false; - - return; -} - -int LLWebRTCVoiceClient::voiceRecordBuffer() -{ - LLSD timeoutResult(LLSDMap("recplay", "stop")); - - LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL; - - LLSD result; - - captureBufferRecordStartSendMessage(); - notifyVoiceFontObservers(); - - do - { - result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - } while (!result.has("recplay")); - - mCaptureBufferRecorded = true; - - captureBufferRecordStopSendMessage(); - mCaptureBufferRecording = false; - - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); - - return true; - /*TODO expand return to move directly into play*/ -} - -int LLWebRTCVoiceClient::voicePlaybackBuffer() -{ - LLSD timeoutResult(LLSDMap("recplay", "stop")); - - LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL; - - LLSD result; - - do - { - captureBufferPlayStartSendMessage(mPreviewVoiceFont); - - // Store the voice font being previewed, so that we know to restart if it changes. - mPreviewVoiceFontLast = mPreviewVoiceFont; - - do - { - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); - - result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - } while (!result.has("recplay")); - - if (result["recplay"] == "playback") - continue; // restart playback... May be a font change. - - break; - } while (true); - - // Stop playing. - captureBufferPlayStopSendMessage(); - mCaptureBufferPlaying = false; - - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); - - return true; -} - - bool LLWebRTCVoiceClient::performMicTuning() { LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; @@ -1663,191 +1456,11 @@ void LLWebRTCVoiceClient::closeSocket(void) mAccountLoggedIn = false; } -void LLWebRTCVoiceClient::loginSendMessage() -{ - std::ostringstream stream; - - bool autoPostCrashDumps = gSavedSettings.getBOOL("WebRTCAutoPostCrashDumps"); - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << mAccountName << "" - << "" << mAccountPassword << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "VerifyAnswer" - << "false" - << "0" - << "Application" - << "5" - << (autoPostCrashDumps?"true":"") - << "\n\n\n"; - - LL_INFOS("Voice") << "Attempting voice login" << LL_ENDL; - writeString(stream.str()); -} - void LLWebRTCVoiceClient::logout() { // Ensure that we'll re-request provisioning before logging in again mAccountPassword.clear(); - - logoutSendMessage(); -} - -void LLWebRTCVoiceClient::logoutSendMessage() -{ - if(mAccountLoggedIn) - { - LL_INFOS("Voice") << "Attempting voice logout" << LL_ENDL; - std::ostringstream stream; - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - mAccountLoggedIn = false; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::sessionGroupCreateSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "Normal" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) -{ - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI - << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" - << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"Session.Create.1\">" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" << session->mSIPURI << ""; - - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~"; - - if(!session->mHash.empty()) - { - stream - << "" << LLURI::escape(session->mHash, allowed_chars) << "" - << "SHA1UserName"; - } - - stream - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << font_index << "" - << "" << mChannelName << "" - << "\n\n\n"; - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) -{ - LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; - - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::string password; - if(!session->mHash.empty()) - { - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~" - ; - password = LLURI::escape(session->mHash, allowed_chars); - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mSIPURI << "" - << "" << mChannelName << "" - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << font_index << "" - << "" << password << "" - << "SHA1UserName" - << "\n\n\n" - ; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t &session) -{ - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle - << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" - << LL_ENDL; - - session->mMediaConnectInProgress = true; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.MediaConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "" << font_index << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session) -{ - LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.TextConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); + mAccountLoggedIn = false; } void LLWebRTCVoiceClient::sessionTerminate() @@ -1868,27 +1481,7 @@ void LLWebRTCVoiceClient::leaveAudioSession() { LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - if(!mAudioSession->mHandle.empty()) - { - -#if RECORD_EVERYTHING - // HACK: for testing only - // Save looped recording - std::string savepath("/tmp/WebRTCrecording"); - { - 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; - } - recordingLoopSave(savepath); -#endif - - sessionMediaDisconnectSendMessage(mAudioSession); - } - else + if(mAudioSession->mHandle.empty()) { LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; } @@ -1900,55 +1493,6 @@ void LLWebRTCVoiceClient::leaveAudioSession() sessionTerminate(); } -void LLWebRTCVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &session) -{ - std::ostringstream stream; - - sessionGroupTerminateSendMessage(session); - return; - /* - LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); - */ -} - -void LLWebRTCVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session) -{ - std::ostringstream stream; - sessionGroupTerminateSendMessage(session); - return; - /* - LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); - */ - -} - void LLWebRTCVoiceClient::clearCaptureDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; @@ -2046,58 +1590,6 @@ bool LLWebRTCVoiceClient::inTuningMode() return mIsInTuningMode; } -void LLWebRTCVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) -{ - mTuningAudioFile = name; - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "" << (loop?"1":"0") << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::tuningRenderStopSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::tuningCaptureStartSendMessage(int loop) -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "-1" - << "" << loop << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::tuningCaptureStopSendMessage() -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); - - mTuningEnergy = 0.0f; -} - void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) { int scaled_volume = scale_mic_volume(volume); @@ -2320,73 +1812,6 @@ void LLWebRTCVoiceClient::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! - * - * From the WebRTC Docs: - * - * VadAuto: A flag indicating if the automatic VAD is enabled (1) or disabled (0) - * - * VadHangover: The time (in milliseconds) that it takes - * for the VAD to switch back to silence from speech mode after the last speech - * frame has been detected. - * - * VadNoiseFloor: 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 - * long speech classifiable as noise. - * - * 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, - * while 100 is 'least sensitive') - */ -void LLWebRTCVoiceClient::setupVADParams(unsigned int vad_auto, - unsigned int vad_hangover, - unsigned int vad_noise_floor, - unsigned int vad_sensitivity) -{ - std::ostringstream stream; - - LL_INFOS("Voice") << "Setting the automatic VAD to " - << (vad_auto ? "True" : "False") - << " and discrete values to" - << " VadHangover = " << vad_hangover - << ", VadSensitivity = " << vad_sensitivity - << ", VadNoiseFloor = " << vad_noise_floor - << LL_ENDL; - - // Create a request to set the VAD parameters: - stream << "" - << "" << vad_auto << "" - << "" << vad_hangover << "" - << "" << vad_sensitivity << "" - << "" << vad_noise_floor << "" - << "\n\n\n"; - - if (!stream.str().empty()) - { - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::onVADSettingsChange() -{ - // pick up the VAD variables (one of which was changed) - unsigned int vad_auto = gSavedSettings.getU32("WebRTCVadAuto"); - unsigned int vad_hangover = gSavedSettings.getU32("WebRTCVadHangover"); - unsigned int vad_noise_floor = gSavedSettings.getU32("WebRTCVadNoiseFloor"); - unsigned int vad_sensitivity = gSavedSettings.getU32("WebRTCVadSensitivity"); - - // build a VAD params change request and send it to SLVoice - setupVADParams(vad_auto, vad_hangover, vad_noise_floor, vad_sensitivity); -} - ///////////////////////////// // WebRTC Signaling Handlers void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) @@ -2612,339 +2037,22 @@ void LLWebRTCVoiceClient::OnRenegotiationNeeded() setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } -///////////////////////////// -// Response/Event handlers - -void LLWebRTCVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) -{ - LLSD result = LLSD::emptyMap(); - - if(statusCode == 0) +void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) { - // Connector created, move forward. - if (connectorHandle == LLWebRTCSecurity::getInstance()->connectorHandle()) - { - LL_INFOS("Voice") << "Voice connector succeeded, WebRTC SDK version is " << versionID << " connector handle " << connectorHandle << LL_ENDL; - mVoiceVersion.serverVersion = versionID; - mConnectorEstablished = true; - mTerminateDaemon = false; - - result["connector"] = LLSD::Boolean(true); - } - else - { - // This shouldn't happen - we are somehow out of sync with SLVoice - // or possibly there are two things trying to run SLVoice at once - // or someone is trying to hack into it. - LL_WARNS("Voice") << "Connector returned wrong handle " - << "(" << connectorHandle << ")" - << " expected (" << LLWebRTCSecurity::getInstance()->connectorHandle() << ")" - << LL_ENDL; - result["connector"] = LLSD::Boolean(false); - // Give up. - mTerminateDaemon = true; - } - } - else if (statusCode == 10028) // web request timeout prior to login - { - // 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 - { - // 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 - { - LL_WARNS("Voice") << "Voice connection failure ("<< statusCode << "): " << statusString << LL_ENDL; - mTerminateDaemon = true; - result["connector"] = LLSD::Boolean(false); - } - - mWebRTCPump.post(result); -} + sessionStatePtr_t oldSession = mAudioSession; -void LLWebRTCVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) -{ - LLSD result = LLSD::emptyMap(); + mAudioSession = session; + mAudioSessionChanged = true; - 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. - LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - result["login"] = LLSD::String("retry"); - } - else if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - result["login"] = LLSD::String("failed"); + // The old session may now need to be deleted. + reapSession(oldSession); } - else - { - // Login succeeded, move forward. - mAccountLoggedIn = true; - mNumberOfAliases = numberOfAliases; - result["login"] = LLSD::String("response_ok"); - } - - mWebRTCPump.post(result); - -} - -void LLWebRTCVoiceClient::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->mErrorStatusString = statusString; - if(session == mAudioSession) - { - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "failed") - ("reason", LLSD::Integer(statusCode))); - - mWebRTCPump.post(WebRTCevent); - } - else - { - reapSession(session); - } - } - } - else - { - LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "created")); - - mWebRTCPump.post(WebRTCevent); - } -} - -void LLWebRTCVoiceClient::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->mErrorStatusString = statusString; - if(session == mAudioSession) - { - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "failed")); - - mWebRTCPump.post(WebRTCevent); - } - else - { - reapSession(session); - } - } - } - else - { - LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } - - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "added")); - - mWebRTCPump.post(WebRTCevent); - - } -} - -void LLWebRTCVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) -{ - sessionStatePtr_t session(findSession(requestId)); - // 1026 is session already has media, somehow mediaconnect was called twice on the same session. - // set the session info to reflect that the user is already connected. - if (statusCode == 1026) - { - session->mVoiceActive = true; - session->mMediaConnectInProgress = false; - session->mMediaStreamState = streamStateConnected; - //session->mTextStreamState = streamStateConnected; - session->mErrorStatusCode = 0; - } - else if (statusCode != 0) - { - LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; - if (session) - { - session->mMediaConnectInProgress = false; - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - } - } - else - { - LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; - } -} - -void LLWebRTCVoiceClient::logoutResponse(int statusCode, std::string &statusString) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; - // Should this ever fail? do we care if it does? - } - LLSD WebRTCevent(LLSDMap("logout", LLSD::Boolean(true))); - - mWebRTCPump.post(WebRTCevent); -} - -void LLWebRTCVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) -{ - if(statusCode != 0) - { - 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 WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); - - mWebRTCPump.post(WebRTCevent); -} - -void LLWebRTCVoiceClient::sessionAddedEvent( - std::string &uriString, - std::string &alias, - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool isChannel, - bool incoming, - std::string &nameString, - std::string &applicationString) -{ - sessionStatePtr_t session; - - LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; - - session = addSession(uriString, sessionHandle); - if(session) - { - session->mGroupHandle = sessionGroupHandle; - 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) - } - 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, session->mCallerID.asString()); - } - else - { - 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 = 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) - { - // If we got here, we don't have a proper name. Initiate a lookup. - lookupName(session->mCallerID); - } - } - } -} - -void LLWebRTCVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) -{ - LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; - -#if USE_SESSION_GROUPS - if(mMainSessionGroupHandle.empty()) - { - // This is the first (i.e. "main") session group. Save its handle. - mMainSessionGroupHandle = sessionGroupHandle; - } - else - { - LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; - } -#endif -} - -void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) -{ - LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; - if(mAudioSession != session) - { - sessionStatePtr_t oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(mIsJoiningSession) + + // This is the session we're joining. + if(mIsJoiningSession) { LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) ("session", "joined")); @@ -2975,53 +2083,12 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::sessionRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle) -{ - LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; - - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - leftAudioSession(session); - - // 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); - } - else - { - // Already reaped this session. - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; - } - -} - void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) { - if(session->mCreateInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; - } - else if(session->mMediaConnectInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; - } - else if(session == mAudioSession) + if(session == mAudioSession) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; } @@ -3077,526 +2144,79 @@ void LLWebRTCVoiceClient::leftAudioSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::accountLoginStateChangeEvent( - std::string &accountHandle, - int statusCode, - std::string &statusString, - int state) -{ - LLSD levent = LLSD::emptyMap(); - - /* - According to Mike S., status codes for this event are: - login_state_logged_out=0, - login_state_logged_in = 1, - login_state_logging_in = 2, - login_state_logging_out = 3, - login_state_resetting = 4, - login_state_error=100 - */ - - LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; - switch(state) - { - case 1: - levent["login"] = LLSD::String("account_login"); - - mWebRTCPump.post(levent); - break; - case 2: - break; - - case 3: - levent["login"] = LLSD::String("account_loggingOut"); - - mWebRTCPump.post(levent); - break; - case 4: - break; - - case 100: - LL_WARNS("Voice") << "account state event error" << LL_ENDL; - break; - - case 0: - levent["login"] = LLSD::String("account_logout"); - - mWebRTCPump.post(levent); - break; - - default: - //Used to be a commented out warning - LL_WARNS("Voice") << "unknown account state event: " << state << LL_ENDL; - break; - } -} -void LLWebRTCVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) +void LLWebRTCVoiceClient::muteListChanged() { - LLSD result; - - if (mediaCompletionType == "AuxBufferAudioCapture") - { - mCaptureBufferRecording = false; - result["recplay"] = "end"; - } - else if (mediaCompletionType == "AuxBufferAudioRender") + // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. + if(mAudioSession) { - // Ignore all but the last stop event - if (--mPlayRequestCount <= 0) + participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + + for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) { - mCaptureBufferPlaying = false; - result["recplay"] = "end"; -// result["recplay"] = "done"; - } - } - else - { - LL_WARNS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; + participantStatePtr_t p(iter->second); + + // Check to see if this participant is on the mute list already + if(p->updateMuteState()) + mAudioSession->mVolumeDirty = true; + } } +} - if (!result.isUndefined()) - mWebRTCPump.post(result); +///////////////////////////// +// Managing list of participants +LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : + mURI(agent_id.asString()), + mAvatarID(agent_id), + mPTT(false), + mIsSpeaking(false), + mIsModeratorMuted(false), + mLastSpokeTimestamp(0.f), + mPower(0.f), + mVolume(LLVoiceClient::VOLUME_DEFAULT), + mUserVolume(0), + mOnMuteList(false), + mVolumeSet(false), + mVolumeDirty(false), + mAvatarIDValid(false), + mIsSelf(false) +{ } -void LLWebRTCVoiceClient::mediaStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - int statusCode, - std::string &statusString, - int state, - bool incoming) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) { - sessionStatePtr_t session(findSession(sessionHandle)); - - LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; + participantStatePtr_t result; - if(session) + participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); + + + if (iter != mParticipantsByUUID.end()) { - // We know about this session + result = iter->second; + } - // Save the state for later use - session->mMediaStreamState = state; + if(!result) + { + // participant isn't already in one list or the other. + result.reset(new participantState(agent_id)); + mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); + mParticipantsChanged = true; + + result->mAvatarIDValid = true; + result->mAvatarID = agent_id; - switch(statusCode) - { - case 0: - case HTTP_OK: - // generic success - // Don't change the saved error code (it may have been set elsewhere) - break; - default: - // save the status code for later - session->mErrorStatusCode = statusCode; - break; - } + if(result->updateMuteState()) + { + mMuteDirty = true; + } + + mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); - switch(state) + if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) { - case streamStateDisconnecting: - case streamStateIdle: - // Standard "left audio session", WebRTC state 'disconnected' - session->mVoiceActive = false; - session->mMediaConnectInProgress = false; - leftAudioSession(session); - break; - - case streamStateConnected: - session->mVoiceActive = true; - session->mMediaConnectInProgress = false; - joinedAudioSession(session); - case streamStateConnecting: // do nothing, but prevents a warning getting into the logs. - break; - - case streamStateRinging: - if(incoming) - { - // Send the voice chat invite to the GUI layer - // TODO: Question: Should we correlate with the mute list here? - session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); - session->mVoiceInvitePending = true; - if(session->mName.empty()) - { - lookupName(session->mCallerID); - } - else - { - // Act like we just finished resolving the name - avatarNameResolved(session->mCallerID, session->mName); - } - } - break; - - default: - LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; - break; - - } - - } - else - { - // session disconnectintg and disconnected events arriving after we have already left the session. - LL_DEBUGS("Voice") << "session " << sessionHandle << " not found"<< LL_ENDL; - } -} - -void LLWebRTCVoiceClient::participantAddedEvent( - 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)); - if(session) - { - participantStatePtr_t participant(session->addParticipant(LLUUID(uriString))); - if(participant) - { - participant->mAccountName = nameString; - - LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - - if(participant->mAvatarIDValid) - { - // Initiate a lookup - lookupName(participant->mAvatarID); - } - else - { - // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. - std::string namePortion = displayNameString; - - if(namePortion.empty()) - { - // 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); - } - } - } -} - -void LLWebRTCVoiceClient::participantRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString) -{ - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - participantStatePtr_t participant(session->findParticipant(uriString)); - if(participant) - { - session->removeParticipant(participant); - } - else - { - LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; - } - } - else - { - // a late arriving event on a session we have already left. - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - -void LLWebRTCVoiceClient::messageEvent( - std::string &sessionHandle, - std::string &uriString, - std::string &alias, - std::string &messageHeader, - std::string &messageBody, - std::string &applicationString) -{ - LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; -// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; - - LL_INFOS("Voice") << "WebRTC raw message:" << std::endl << messageBody << LL_ENDL; - - if(messageHeader.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos) - { - std::string message; - - { - const std::string startMarker = ", try looking for a 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) - { - // Strip out the tag - message.erase(start, (end + 1) - start); - } - else - { - // Avoid an infinite loop - break; - } - } - } - - // Decode ampersand-escaped chars - { - std::string::size_type mark = 0; - - // The text may contain text encoded with <, >, and & - mark = 0; - while((mark = message.find("<", mark)) != std::string::npos) - { - message.replace(mark, 4, "<"); - mark += 1; - } - - mark = 0; - while((mark = message.find(">", mark)) != std::string::npos) - { - message.replace(mark, 4, ">"); - mark += 1; - } - - mark = 0; - while((mark = message.find("&", mark)) != std::string::npos) - { - message.replace(mark, 5, "&"); - 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) - { - bool is_do_not_disturb = gAgent.isDoNotDisturb(); - bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); - bool is_linden = LLMuteList::isLinden(session->mName); - LLChat chat; - - chat.mMuted = is_muted && !is_linden; - - if(!chat.mMuted) - { - chat.mFromID = session->mCallerID; - chat.mFromName = session->mName; - chat.mSourceType = CHAT_SOURCE_AGENT; - - if(is_do_not_disturb && !is_linden) - { - // 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, - session->mName.c_str(), - message.c_str(), - false, - LLStringUtil::null, // default arg - IM_NOTHING_SPECIAL, // default arg - 0, // default arg - LLUUID::null, // default arg - LLVector3::zero); // default arg - } - } - } -} - -void LLWebRTCVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) -{ - sessionStatePtr_t session(findSession(sessionHandle)); - - if(session) - { - participantStatePtr_t participant(session->findParticipant(uriString)); - if(participant) - { - if (!stricmp(notificationType.c_str(), "Typing")) - { - // Other end started typing - // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). - // It requires some info for the message, which we don't have here. - } - else if (!stricmp(notificationType.c_str(), "NotTyping")) - { - // Other end stopped typing - // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). - // It requires some info for the message, which we don't have here. - } - else - { - LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; - } -} - -void LLWebRTCVoiceClient::voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id) -{ - // We don't generally need to process this. However, one occurence is when we first connect, and so it is the - // earliest opportunity to learn what we're connected to. - if (statusCode) - { - LL_WARNS("Voice") << "VoiceServiceConnectionStateChangedEvent statusCode: " << statusCode << - "statusString: " << statusString << LL_ENDL; - return; - } - if (build_id.empty()) - { - return; - } - mVoiceVersion.mBuildVersion = build_id; -} - -void LLWebRTCVoiceClient::auxAudioPropertiesEvent(F32 energy) -{ - LL_DEBUGS("VoiceEnergy") << "got energy " << energy << LL_ENDL; - mTuningEnergy = energy; -} - -void LLWebRTCVoiceClient::muteListChanged() -{ - // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. - 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; - } - } -} - -///////////////////////////// -// Managing list of participants -LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : - mURI(agent_id.asString()), - mAvatarID(agent_id), - mPTT(false), - mIsSpeaking(false), - mIsModeratorMuted(false), - mLastSpokeTimestamp(0.f), - mPower(0.f), - mVolume(LLVoiceClient::VOLUME_DEFAULT), - mUserVolume(0), - mOnMuteList(false), - mVolumeSet(false), - mVolumeDirty(false), - mAvatarIDValid(false), - mIsSelf(false) -{ -} - -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)); - mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); - mParticipantsChanged = true; - - result->mAvatarIDValid = true; - result->mAvatarID = agent_id; - - if(result->updateMuteState()) - { - mMuteDirty = true; - } - - mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); - - if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) - { - result->mVolumeDirty = true; - mVolumeDirty = true; + result->mVolumeDirty = true; + mVolumeDirty = true; } LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; @@ -3894,7 +2514,6 @@ bool LLWebRTCVoiceClient::switchChannel( LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; mNextAudioSession = addSession(uri); - mNextAudioSession->mHash = hash; mNextAudioSession->mIsSpatial = spatial; mNextAudioSession->mReconnect = !no_reconnect; mNextAudioSession->mIsP2P = is_p2p; @@ -3931,11 +2550,9 @@ void LLWebRTCVoiceClient::setNonSpatialChannel( } bool LLWebRTCVoiceClient::setSpatialChannel( - const std::string &uri, - const std::string &credentials) + const std::string &uri, const std::string &credentials) { mSpatialSessionURI = uri; - mSpatialSessionCredentials = credentials; mAreaVoiceDisabled = mSpatialSessionURI.empty(); LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; @@ -3948,7 +2565,7 @@ bool LLWebRTCVoiceClient::setSpatialChannel( } else { - return switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + return switchChannel(mSpatialSessionURI, true, false, false); } } @@ -4003,13 +2620,7 @@ BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) 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. - if(session->mSynthesizedCallerID) - result = FALSE; - } - else + if(!session) { // Didn't find a matching session -- check the current audio session for a matching participant if(mAudioSession) @@ -4058,11 +2669,6 @@ BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) void LLWebRTCVoiceClient::declineInvite(std::string &sessionHandle) { - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - sessionMediaDisconnectSendMessage(session); - } } void LLWebRTCVoiceClient::leaveNonSpatialChannel() @@ -4643,108 +3249,6 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() return mAreaVoiceDisabled; } -void LLWebRTCVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; - - if(!mMainSessionGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << deltaFramesPerControlFrame << "" - << "" << "" << "" - << "false" - << "" << seconds << "" - << "\n\n\n"; - - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::recordingLoopSave(const std::string& filename) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Flush" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::recordingStop() -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::filePlaybackStart(const std::string& filename) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::filePlaybackStop() -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::filePlaybackSetPaused(bool paused) -{ - // TODO: Implement once WebRTC gives me a sample -} - -void LLWebRTCVoiceClient::filePlaybackSetMode(bool vox, float speed) -{ - // TODO: Implement once WebRTC gives me a sample -} - //------------------------------------------------------------------------ std::set LLWebRTCVoiceClient::sessionState::mSession; @@ -4752,11 +3256,6 @@ std::set LLWebRTCVoiceClient::session LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), mMediaStreamState(streamStateUnknown), - mCreateInProgress(false), - mMediaConnectInProgress(false), - mVoiceInvitePending(false), - mTextInvitePending(false), - mSynthesizedCallerID(false), mIsChannel(false), mIsSpatial(false), mIsP2P(false), @@ -4796,13 +3295,13 @@ bool LLWebRTCVoiceClient::sessionState::isCallBackPossible() // This may change to be explicitly specified by WebRTC in the future... // Currently, only PSTN P2P calls cannot be returned. // Conveniently, this is also the only case where we synthesize a caller UUID. - return !mSynthesizedCallerID; + return false; } bool LLWebRTCVoiceClient::sessionState::isTextIMPossible() { // This may change to be explicitly specified by WebRTC in the future... - return !mSynthesizedCallerID; + return false; } @@ -4820,20 +3319,6 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matc return result; } -/*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchCreatingSessionByURI(const std::string &uri) -{ - sessionStatePtr_t result; - - // *TODO: My kingdom for a lambda! - std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCreatingURI, _1, uri)); - - if (it != mSession.end()) - result = (*it).lock(); - - return result; -} - /*static*/ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByURI(const std::string &uri) { @@ -4880,7 +3365,7 @@ bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceCli { ptr_t aLock(a.lock()); - return aLock ? (aLock->mCreateInProgress && (aLock->mSIPURI == uri)) : false; + return aLock ? (aLock->mSIPURI == uri) : false; } bool LLWebRTCVoiceClient::sessionState::testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) @@ -4929,13 +3414,6 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const st return result; } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) -{ - sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri); - - return result; -} - LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const LLUUID &participant_id) { sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); @@ -5305,27 +3783,6 @@ void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::se { // this session's "caller ID" just resolved. Fill in the name. session->mName = name; - if (session->mTextInvitePending) - { - session->mTextInvitePending = false; - - // 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, - session->mCallerID, - session->mName, - IM_SESSION_P2P_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE, - session->mHandle, - session->mSIPURI); - } - } } @@ -5334,775 +3791,6 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); } -bool LLWebRTCVoiceClient::setVoiceEffect(const LLUUID& id) -{ - if (!mAudioSession) - { - return false; - } - - if (!id.isNull()) - { - if (mVoiceFontMap.empty()) - { - LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; - return false; - } - else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) - { - LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; - return false; - } - } - - // *TODO: Check for expired fonts? - mAudioSession->mVoiceFontID = id; - - // *TODO: Separate voice font defaults for spatial chat and IM? - gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); - - sessionSetVoiceFontSendMessage(mAudioSession); - notifyVoiceFontObservers(); - - return true; -} - -const LLUUID LLWebRTCVoiceClient::getVoiceEffect() -{ - return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; -} - -LLSD LLWebRTCVoiceClient::getVoiceEffectProperties(const LLUUID& id) -{ - LLSD sd; - - voice_font_map_t::iterator iter = mVoiceFontMap.find(id); - if (iter != mVoiceFontMap.end()) - { - sd["template_only"] = false; - } - else - { - // Voice effect is not in the voice font map, see if there is a template - iter = mVoiceFontTemplateMap.find(id); - if (iter == mVoiceFontTemplateMap.end()) - { - LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; - return sd; - } - sd["template_only"] = true; - } - - voiceFontEntry *font = iter->second; - sd["name"] = font->mName; - sd["expiry_date"] = font->mExpirationDate; - sd["is_new"] = font->mIsNew; - - return sd; -} - -LLWebRTCVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : - mID(id), - mFontIndex(0), - mFontType(VOICE_FONT_TYPE_NONE), - mFontStatus(VOICE_FONT_STATUS_NONE), - mIsNew(false) -{ - mExpiryTimer.stop(); - mExpiryWarningTimer.stop(); -} - -LLWebRTCVoiceClient::voiceFontEntry::~voiceFontEntry() -{ -} - -void LLWebRTCVoiceClient::refreshVoiceEffectLists(bool clear_lists) -{ - if (clear_lists) - { - mVoiceFontsReceived = false; - deleteAllVoiceFonts(); - deleteVoiceFontTemplates(); - } - - accountGetSessionFontsSendMessage(); - accountGetTemplateFontsSendMessage(); -} - -const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectList() const -{ - return mVoiceFontList; -} - -const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectTemplateList() const -{ - return mVoiceFontTemplateList; -} - -void LLWebRTCVoiceClient::addVoiceFont(const S32 font_index, - const std::string &name, - const std::string &description, - const LLDate &expiration_date, - bool has_expired, - const S32 font_type, - const S32 font_status, - const bool template_font) -{ - // WebRTC SessionFontIDs are not guaranteed to remain the same between - // sessions or grids so use a UUID for the name. - - // If received name is not a UUID, fudge one by hashing the name and type. - LLUUID font_id; - if (LLUUID::validate(name)) - { - font_id = LLUUID(name); - } - else - { - font_id.generate(STRINGIZE(font_type << ":" << name)); - } - - voiceFontEntry *font = NULL; - - voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; - voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; - - // Check whether we've seen this font before. - voice_font_map_t::iterator iter = font_map.find(font_id); - bool new_font = (iter == font_map.end()); - - // Override the has_expired flag if we have passed the expiration_date as a double check. - if (expiration_date.secondsSinceEpoch() < (LLDate::now().secondsSinceEpoch() + VOICE_FONT_EXPIRY_INTERVAL)) - { - has_expired = true; - } - - if (has_expired) - { - LL_DEBUGS("VoiceFont") << "Expired " << (template_font ? "Template " : "") - << expiration_date.asString() << " " << font_id - << " (" << font_index << ") " << name << LL_ENDL; - - // Remove existing session fonts that have expired since we last saw them. - if (!new_font && !template_font) - { - deleteVoiceFont(font_id); - } - return; - } - - if (new_font) - { - // If it is a new font create a new entry. - font = new voiceFontEntry(font_id); - } - else - { - // Not a new font, update the existing entry - font = iter->second; - } - - if (font) - { - font->mFontIndex = font_index; - // Use the description for the human readable name if available, as the - // "name" may be a UUID. - font->mName = description.empty() ? name : description; - font->mFontType = font_type; - font->mFontStatus = font_status; - - // If the font is new or the expiration date has changed the expiry timers need updating. - if (!template_font && (new_font || font->mExpirationDate != expiration_date)) - { - font->mExpirationDate = expiration_date; - - // Set the expiry timer to trigger a notification when the voice font can no longer be used. - font->mExpiryTimer.start(); - font->mExpiryTimer.setExpiryAt(expiration_date.secondsSinceEpoch() - VOICE_FONT_EXPIRY_INTERVAL); - - // Set the warning timer to some interval before actual expiry. - S32 warning_time = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); - if (warning_time != 0) - { - font->mExpiryWarningTimer.start(); - F64 expiry_time = (expiration_date.secondsSinceEpoch() - (F64)warning_time); - font->mExpiryWarningTimer.setExpiryAt(expiry_time - VOICE_FONT_EXPIRY_INTERVAL); - } - else - { - // Disable the warning timer. - font->mExpiryWarningTimer.stop(); - } - - // Only flag new session fonts after the first time we have fetched the list. - if (mVoiceFontsReceived) - { - font->mIsNew = true; - mVoiceFontsNew = true; - } - } - - LL_DEBUGS("VoiceFont") << (template_font ? "Template " : "") - << font->mExpirationDate.asString() << " " << font->mID - << " (" << font->mFontIndex << ") " << name << LL_ENDL; - - if (new_font) - { - font_map.insert(voice_font_map_t::value_type(font->mID, font)); - font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); - } - - mVoiceFontListDirty = true; - - // Debugging stuff - - if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) - { - LL_WARNS("VoiceFont") << "Unknown voice font type: " << font_type << LL_ENDL; - } - if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) - { - LL_WARNS("VoiceFont") << "Unknown voice font status: " << font_status << LL_ENDL; - } - } -} - -void LLWebRTCVoiceClient::expireVoiceFonts() -{ - // *TODO: If we are selling voice fonts in packs, there are probably - // going to be a number of fonts with the same expiration time, so would - // be more efficient to just keep a list of expiration times rather - // than checking each font individually. - - bool have_expired = false; - bool will_expire = false; - bool expired_in_use = false; - - LLUUID current_effect = LLVoiceClient::instance().getVoiceEffectDefault(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) - { - voiceFontEntry* voice_font = iter->second; - LLFrameTimer& expiry_timer = voice_font->mExpiryTimer; - LLFrameTimer& warning_timer = voice_font->mExpiryWarningTimer; - - // Check for expired voice fonts - if (expiry_timer.getStarted() && expiry_timer.hasExpired()) - { - // Check whether it is the active voice font - if (voice_font->mID == current_effect) - { - // Reset to no voice effect. - setVoiceEffect(LLUUID::null); - expired_in_use = true; - } - - LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " has expired." << LL_ENDL; - deleteVoiceFont(voice_font->mID); - have_expired = true; - } - - // Check for voice fonts that will expire in less that the warning time - if (warning_timer.getStarted() && warning_timer.hasExpired()) - { - LL_DEBUGS("VoiceFont") << "Voice Font " << voice_font->mName << " will expire soon." << LL_ENDL; - will_expire = true; - warning_timer.stop(); - } - } - - LLSD args; - args["URL"] = LLTrans::getString("voice_morphing_url"); - args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); - - // Give a notification if any voice fonts have expired. - if (have_expired) - { - if (expired_in_use) - { - LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); - } - else - { - LLNotificationsUtil::add("VoiceEffectsExpired", args); - } - - // Refresh voice font lists in the UI. - notifyVoiceFontObservers(); - } - - // Give a warning notification if any voice fonts are due to expire. - if (will_expire) - { - S32Seconds seconds(gSavedSettings.getS32("VoiceEffectExpiryWarningTime")); - args["INTERVAL"] = llformat("%d", LLUnit(seconds).value()); - - LLNotificationsUtil::add("VoiceEffectsWillExpire", args); - } -} - -void LLWebRTCVoiceClient::deleteVoiceFont(const LLUUID& id) -{ - // Remove the entry from the voice font list. - voice_effect_list_t::iterator list_iter = mVoiceFontList.begin(); - while (list_iter != mVoiceFontList.end()) - { - if (list_iter->second == id) - { - LL_DEBUGS("VoiceFont") << "Removing " << id << " from the voice font list." << LL_ENDL; - list_iter = mVoiceFontList.erase(list_iter); - mVoiceFontListDirty = true; - } - else - { - ++list_iter; - } - } - - // Find the entry in the voice font map and erase its data. - voice_font_map_t::iterator map_iter = mVoiceFontMap.find(id); - if (map_iter != mVoiceFontMap.end()) - { - delete map_iter->second; - } - - // Remove the entry from the voice font map. - mVoiceFontMap.erase(map_iter); -} - -void LLWebRTCVoiceClient::deleteAllVoiceFonts() -{ - mVoiceFontList.clear(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) - { - delete iter->second; - } - mVoiceFontMap.clear(); -} - -void LLWebRTCVoiceClient::deleteVoiceFontTemplates() -{ - mVoiceFontTemplateList.clear(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) - { - delete iter->second; - } - mVoiceFontTemplateMap.clear(); -} - -S32 LLWebRTCVoiceClient::getVoiceFontIndex(const LLUUID& id) const -{ - S32 result = 0; - if (!id.isNull()) - { - voice_font_map_t::const_iterator it = mVoiceFontMap.find(id); - if (it != mVoiceFontMap.end()) - { - result = it->second->mFontIndex; - } - else - { - LL_WARNS("VoiceFont") << "Selected voice font " << id << " is not available." << LL_ENDL; - } - } - return result; -} - -S32 LLWebRTCVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const -{ - S32 result = 0; - if (!id.isNull()) - { - voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); - if (it != mVoiceFontTemplateMap.end()) - { - result = it->second->mFontIndex; - } - else - { - LL_WARNS("VoiceFont") << "Selected voice font template " << id << " is not available." << LL_ENDL; - } - } - return result; -} - -void LLWebRTCVoiceClient::accountGetSessionFontsSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("VoiceFont") << "Requesting voice font list." << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::accountGetTemplateFontsSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("VoiceFont") << "Requesting voice font template list." << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session) -{ - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("VoiceFont") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; - - std::ostringstream stream; - - stream - << "" - << "" << session->mHandle << "" - << "" << font_index << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) -{ - if (mIsWaitingForFonts) - { - // *TODO: We seem to get multiple events of this type. Should figure a way to advance only after - // receiving the last one. - LLSD result(LLSDMap("voice_fonts", LLSD::Boolean(true))); - - mWebRTCPump.post(result); - } - notifyVoiceFontObservers(); - mVoiceFontsReceived = true; -} - -void LLWebRTCVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) -{ - // Voice font list entries were updated via addVoiceFont() during parsing. - notifyVoiceFontObservers(); -} -void LLWebRTCVoiceClient::addObserver(LLVoiceEffectObserver* observer) -{ - mVoiceFontObservers.insert(observer); -} - -void LLWebRTCVoiceClient::removeObserver(LLVoiceEffectObserver* observer) -{ - mVoiceFontObservers.erase(observer); -} - -// method checks the item in VoiceMorphing menu for appropriate current voice font -bool LLWebRTCVoiceClient::onCheckVoiceEffect(const std::string& voice_effect_name) -{ - LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); - if (NULL != effect_interfacep) - { - const LLUUID& currect_voice_effect_id = effect_interfacep->getVoiceEffect(); - - if (currect_voice_effect_id.isNull()) - { - if (voice_effect_name == "NoVoiceMorphing") - { - return true; - } - } - else - { - const LLSD& voice_effect_props = effect_interfacep->getVoiceEffectProperties(currect_voice_effect_id); - if (voice_effect_props["name"].asString() == voice_effect_name) - { - return true; - } - } - } - - return false; -} - -// method changes voice font for selected VoiceMorphing menu item -void LLWebRTCVoiceClient::onClickVoiceEffect(const std::string& voice_effect_name) -{ - LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); - if (NULL != effect_interfacep) - { - if (voice_effect_name == "NoVoiceMorphing") - { - effect_interfacep->setVoiceEffect(LLUUID()); - return; - } - const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); - if (!effect_list.empty()) - { - for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) - { - if (voice_effect_name == it->first) - { - effect_interfacep->setVoiceEffect(it->second); - return; - } - } - } - } -} - -// it updates VoiceMorphing menu items in accordance with purchased properties -void LLWebRTCVoiceClient::updateVoiceMorphingMenu() -{ - if (mVoiceFontListDirty) - { - LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); - if (effect_interfacep) - { - const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); - if (!effect_list.empty()) - { - LLMenuGL * voice_morphing_menup = gMenuBarView->findChildMenuByName("VoiceMorphing", TRUE); - - if (NULL != voice_morphing_menup) - { - S32 items = voice_morphing_menup->getItemCount(); - if (items > 0) - { - voice_morphing_menup->erase(1, items - 3, false); - - S32 pos = 1; - for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) - { - LLMenuItemCheckGL::Params p; - p.name = it->first; - p.label = it->first; - p.on_check.function(boost::bind(&LLWebRTCVoiceClient::onCheckVoiceEffect, this, it->first)); - p.on_click.function(boost::bind(&LLWebRTCVoiceClient::onClickVoiceEffect, this, it->first)); - LLMenuItemCheckGL * voice_effect_itemp = LLUICtrlFactory::create(p); - voice_morphing_menup->insert(pos++, voice_effect_itemp, false); - } - - voice_morphing_menup->needsArrange(); - } - } - } - } - } -} -void LLWebRTCVoiceClient::notifyVoiceFontObservers() -{ - LL_DEBUGS("VoiceFont") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; - - updateVoiceMorphingMenu(); - - for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); - it != mVoiceFontObservers.end();) - { - LLVoiceEffectObserver* observer = *it; - observer->onVoiceEffectChanged(mVoiceFontListDirty); - // In case onVoiceEffectChanged() deleted an entry. - it = mVoiceFontObservers.upper_bound(observer); - } - mVoiceFontListDirty = false; - - // If new Voice Fonts have been added notify the user. - if (mVoiceFontsNew) - { - if (mVoiceFontsReceived) - { - LLNotificationsUtil::add("VoiceEffectsNew"); - } - mVoiceFontsNew = false; - } -} - -void LLWebRTCVoiceClient::enablePreviewBuffer(bool enable) -{ - LLSD result; - mCaptureBufferMode = enable; - - if (enable) - result["recplay"] = "start"; - else - result["recplay"] = "quit"; - - mWebRTCPump.post(result); - - if(mCaptureBufferMode && mIsInChannel) - { - LL_DEBUGS("Voice") << "no channel" << LL_ENDL; - sessionTerminate(); - } -} - -void LLWebRTCVoiceClient::recordPreviewBuffer() -{ - if (!mCaptureBufferMode) - { - LL_DEBUGS("Voice") << "Not in voice effect preview mode, cannot start recording." << LL_ENDL; - mCaptureBufferRecording = false; - return; - } - - mCaptureBufferRecording = true; - - LLSD result(LLSDMap("recplay", "record")); - mWebRTCPump.post(result); -} - -void LLWebRTCVoiceClient::playPreviewBuffer(const LLUUID& effect_id) -{ - if (!mCaptureBufferMode) - { - LL_DEBUGS("Voice") << "Not in voice effect preview mode, no buffer to play." << LL_ENDL; - mCaptureBufferRecording = false; - return; - } - - if (!mCaptureBufferRecorded) - { - // Can't play until we have something recorded! - mCaptureBufferPlaying = false; - return; - } - - mPreviewVoiceFont = effect_id; - mCaptureBufferPlaying = true; - - LLSD result(LLSDMap("recplay", "playback")); - mWebRTCPump.post(result); -} - -void LLWebRTCVoiceClient::stopPreviewBuffer() -{ - mCaptureBufferRecording = false; - mCaptureBufferPlaying = false; - - LLSD result(LLSDMap("recplay", "quit")); - mWebRTCPump.post(result); -} - -bool LLWebRTCVoiceClient::isPreviewRecording() -{ - return (mCaptureBufferMode && mCaptureBufferRecording); -} - -bool LLWebRTCVoiceClient::isPreviewPlaying() -{ - return (mCaptureBufferMode && mCaptureBufferPlaying); -} - -void LLWebRTCVoiceClient::captureBufferRecordStartSendMessage() -{ if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; - - // Start capture - stream - << "" - << "" - << "\n\n\n"; - - // Unmute the mic - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "false" - << "\n\n\n"; - - // Dirty the mute mic state so that it will get reset when we finishing previewing - mMuteMicDirty = true; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::captureBufferRecordStopSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; - - // Mute the mic. Mic mute state was dirtied at recording start, so will be reset when finished previewing. - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "true" - << "\n\n\n"; - - // Stop capture - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) -{ - if(mAccountLoggedIn) - { - // Track how may play requests are sent, so we know how many stop events to - // expect before play actually stops. - ++mPlayRequestCount; - - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; - - S32 font_index = getVoiceFontTemplateIndex(voice_font_id); - LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" << font_index << "" - << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index b33dc26dff..ee7edb3030 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -53,8 +53,6 @@ class LLWebRTCProtocolParser; #include class LLAvatarName; -class LLWebRTCVoiceClientMuteListObserver; - class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, @@ -200,6 +198,40 @@ public: 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 Effect Accessors + //@{ + bool setVoiceEffect(const LLUUID &id) override { return false; } + const LLUUID getVoiceEffect() override { return LLUUID(); } + LLSD getVoiceEffectProperties(const LLUUID &id) override { return LLSD(); } + + void refreshVoiceEffectLists(bool clear_lists) override {}; + const voice_effect_list_t &getVoiceEffectList() const override { return mVoiceEffectList; } + const voice_effect_list_t &getVoiceEffectTemplateList() const override { return mVoiceEffectList; } + + voice_effect_list_t mVoiceEffectList; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + void addObserver(LLVoiceEffectObserver *observer) override {} + void removeObserver(LLVoiceEffectObserver *observer) override {} + //@} + + ////////////////////////////// + /// @name Preview buffer + //@{ + void enablePreviewBuffer(bool enable) override {} + void recordPreviewBuffer() override {} + void playPreviewBuffer(const LLUUID &effect_id = LLUUID::null) override {} + void stopPreviewBuffer() override {} + + bool isPreviewRecording() override { return false; } + bool isPreviewPlaying() override { return false; } + //@} + // authorize the user void userAuthorized(const std::string& user_id, @@ -218,28 +250,7 @@ public: //@} - /// @name LLVoiceEffectInterface virtual implementations - /// @see LLVoiceEffectInterface - //@{ - ////////////////////////// - /// @name Accessors - //@{ - bool setVoiceEffect(const LLUUID& id) override; - const LLUUID getVoiceEffect() override; - LLSD getVoiceEffectProperties(const LLUUID& id) 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 - //@{ - void addObserver(LLVoiceEffectObserver* observer) override; - void removeObserver(LLVoiceEffectObserver* observer) override; - //@} ////////////////////////////// /// @name Devices change notification @@ -279,29 +290,11 @@ public: void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result); - ////////////////////////////// - /// @name Effect preview buffer - //@{ - void enablePreviewBuffer(bool enable) override; - void recordPreviewBuffer() override; - void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; - void stopPreviewBuffer() override; - - bool isPreviewRecording() override; - bool isPreviewPlaying() override; //@} - //@} - - bool onCheckVoiceEffect(const std::string& voice_effect_name); - void onClickVoiceEffect(const std::string& voice_effect_name); - protected: ////////////////////// // WebRTC Specific definitions - - friend class LLWebRTCVoiceClientMuteListObserver; - friend class LLWebRTCVoiceClientFriendsObserver; enum streamState @@ -366,7 +359,6 @@ protected: participantStatePtr_t findParticipantByID(const LLUUID& id); static ptr_t matchSessionByHandle(const std::string &handle); - static ptr_t matchCreatingSessionByURI(const std::string &uri); static ptr_t matchSessionByURI(const std::string &uri); static ptr_t matchSessionByParticipant(const LLUUID &participant_id); @@ -381,7 +373,6 @@ protected: std::string mAlias; std::string mName; std::string mAlternateSIPURI; - std::string mHash; // Channel password std::string mErrorStatusString; std::queue mTextMsgQueue; @@ -389,11 +380,6 @@ protected: LLUUID mCallerID; int mErrorStatusCode; int mMediaStreamState; - bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. - bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. - bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) - bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) - bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) bool mIsSpatial; // True for spatial channels bool mIsP2P; @@ -447,8 +433,6 @@ protected: // 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(); - // write to the tvc - bool writeString(const std::string &str); void connectorCreate(); void connectorShutdown(); @@ -459,21 +443,10 @@ protected: const std::string& account_name, const std::string& password, const std::string& channel_sdp); - void loginSendMessage(); - void logout(); - void logoutSendMessage(); + void logout(); //@} - - //------------------------------------ - // tuning - - void tuningRenderStartSendMessage(const std::string& name, bool loop); - void tuningRenderStopSendMessage(); - - void tuningCaptureStartSendMessage(int duration); - void tuningCaptureStopSendMessage(); //---------------------------------- // devices @@ -489,35 +462,9 @@ protected: void sendLocalAudioUpdates(); ///////////////////////////// - // Response/Event handlers - void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); - void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); - void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); - void logoutResponse(int statusCode, std::string &statusString); - void connectorShutdownResponse(int statusCode, std::string &statusString); - - void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); - void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType); - void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); - void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); - void sessionGroupAddedEvent(std::string &sessionGroupHandle); - void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); - void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); - void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); - void voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id); - 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 ¬ificationType); - + // Event handlers + void muteListChanged(); - - ///////////////////////////// - // VAD changes - // disable auto-VAD and configure VAD parameters explicitly - void setupVADParams(unsigned int vad_auto, unsigned int vad_hangover, unsigned int vad_noise_floor, unsigned int vad_sensitivity); - void onVADSettingsChange(); ///////////////////////////// // Sending updates of current state @@ -539,19 +486,6 @@ protected: ///////////////////////////// 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); participantStatePtr_t addParticipantByID(const LLUUID &id); @@ -568,7 +502,6 @@ protected: #endif void sessionEstablished(); 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()); @@ -611,24 +544,6 @@ protected: typedef std::map 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); - void sessionMediaConnectSendMessage(const sessionStatePtr_t &session); // just joins the audio session - void sessionTextConnectSendMessage(const sessionStatePtr_t &session); // just joins the text session - void sessionTerminateSendMessage(const sessionStatePtr_t &session); - void sessionGroupTerminateSendMessage(const sessionStatePtr_t &session); - void sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session); - // void sessionTextDisconnectSendMessage(sessionState *session); - - - // Pokes the state machine to leave the audio session next time around. void sessionTerminate(); @@ -704,10 +619,6 @@ private: bool waitForChannel(); bool runSession(const sessionStatePtr_t &session); - void recordingAndPlaybackMode(); - int voiceRecordBuffer(); - int voicePlaybackBuffer(); - bool performMicTuning(); //--- /// Clean up objects created during a voice session. @@ -811,14 +722,6 @@ private: Json::Value getPositionAndVolumeUpdateJson(bool force); void sendPositionAndVolumeUpdate(); - void sendFriendsListUpdates(); - -#if 0 - // start a text IM session with the specified user - // This will be asynchronous, the session may be established at a future time. - sessionStatePtr_t startUserIMSession(const LLUUID& uuid); -#endif - void enforceTether(void); bool mSpatialCoordsDirty; @@ -876,87 +779,6 @@ private: friend_observer_set_t mFriendObservers; void notifyFriendObservers(); - // Voice Fonts - - void expireVoiceFonts(); - void deleteVoiceFont(const LLUUID& id); - void deleteAllVoiceFonts(); - void deleteVoiceFontTemplates(); - - S32 getVoiceFontIndex(const LLUUID& id) const; - S32 getVoiceFontTemplateIndex(const LLUUID& id) const; - - void accountGetSessionFontsSendMessage(); - void accountGetTemplateFontsSendMessage(); - void sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session); - - void updateVoiceMorphingMenu(); - void notifyVoiceFontObservers(); - - typedef enum e_voice_font_type - { - VOICE_FONT_TYPE_NONE = 0, - VOICE_FONT_TYPE_ROOT = 1, - VOICE_FONT_TYPE_USER = 2, - VOICE_FONT_TYPE_UNKNOWN - } EVoiceFontType; - - typedef enum e_voice_font_status - { - VOICE_FONT_STATUS_NONE = 0, - VOICE_FONT_STATUS_FREE = 1, - VOICE_FONT_STATUS_NOT_FREE = 2, - VOICE_FONT_STATUS_UNKNOWN - } EVoiceFontStatus; - - struct voiceFontEntry - { - voiceFontEntry(LLUUID& id); - ~voiceFontEntry(); - - LLUUID mID; - S32 mFontIndex; - std::string mName; - LLDate mExpirationDate; - S32 mFontType; - S32 mFontStatus; - bool mIsNew; - - LLFrameTimer mExpiryTimer; - LLFrameTimer mExpiryWarningTimer; - }; - - bool mVoiceFontsReceived; - bool mVoiceFontsNew; - bool mVoiceFontListDirty; - voice_effect_list_t mVoiceFontList; - voice_effect_list_t mVoiceFontTemplateList; - - typedef std::map voice_font_map_t; - voice_font_map_t mVoiceFontMap; - voice_font_map_t mVoiceFontTemplateMap; - - typedef std::set voice_font_observer_set_t; - voice_font_observer_set_t mVoiceFontObservers; - - LLFrameTimer mVoiceFontExpiryTimer; - - - // Audio capture buffer - - void captureBufferRecordStartSendMessage(); - void captureBufferRecordStopSendMessage(); - void captureBufferPlayStartSendMessage(const LLUUID& voice_font_id = LLUUID::null); - void captureBufferPlayStopSendMessage(); - - bool mCaptureBufferMode; // Disconnected from voice channels while using the capture buffer. - bool mCaptureBufferRecording; // A voice sample is being captured. - bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play. - bool mCaptureBufferPlaying; // A voice sample is being played. - - LLTimer mCaptureTimer; - LLUUID mPreviewVoiceFont; - LLUUID mPreviewVoiceFontLast; S32 mPlayRequestCount; bool mIsInTuningMode; bool mIsInChannel; -- cgit v1.2.3 From ebfa44cdb76afb9632556115eef10969912340f5 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 30 Nov 2023 13:14:07 -0800 Subject: Refactor/clean-up WebRTC voice to handle multiple voice streams This is useful for cross-region voice, quick voice switching, etc. --- indra/llwebrtc/llwebrtc.cpp | 79 +- indra/llwebrtc/llwebrtc.h | 8 +- indra/llwebrtc/llwebrtc_impl.h | 7 +- indra/newview/llvoicewebrtc.cpp | 3252 ++++++++++++++------------------------- indra/newview/llvoicewebrtc.h | 398 +++-- 5 files changed, 1438 insertions(+), 2306 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index bf1e04c2f2..b2f5e0212e 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -216,6 +216,7 @@ 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++) { @@ -225,18 +226,32 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id) 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; + tuningRecordingDevice = i; break; } } mTuningDeviceModule->StopRecording(); - mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); + mTuningDeviceModule->SetRecordingDevice(tuningRecordingDevice); mTuningDeviceModule->InitMicrophone(); mTuningDeviceModule->InitRecording(); mTuningDeviceModule->StartRecording(); bool was_peer_recording = false; if (mPeerDeviceModule) { + 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; + mRecordingDevice = i; + break; + } + } was_peer_recording = mPeerDeviceModule->Recording(); if (was_peer_recording) { @@ -259,6 +274,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) [this, id]() { int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); + int16_t tuningPlayoutDevice = 0; for (int16_t i = 0; i < renderDeviceCount; i++) { char name[webrtc::kAdmMaxDeviceNameSize]; @@ -267,7 +283,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) if (id == guid || id == "Default") { RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; - mPlayoutDevice = i; + tuningPlayoutDevice = i; break; } } @@ -277,30 +293,40 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) { mTuningDeviceModule->StopPlayout(); } - bool was_peer_mute = false; if (mPeerDeviceModule) { - mPeerDeviceModule->SpeakerMute(&was_peer_mute); - if (!was_peer_mute) - { - mPeerDeviceModule->SetSpeakerMute(true); - } + mPeerDeviceModule->SetSpeakerMute(true); } - mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mTuningDeviceModule->SetPlayoutDevice(tuningPlayoutDevice); mTuningDeviceModule->InitSpeaker(); mTuningDeviceModule->InitPlayout(); if (was_tuning_playing) { mTuningDeviceModule->StartPlayout(); } + renderDeviceCount = mPeerDeviceModule->PlayoutDevices(); + if (mPeerDeviceModule) { + 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 recording device to " << name << " " << guid << " " << i; + mPlayoutDevice = i; + break; + } + } mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); mPeerDeviceModule->InitSpeaker(); mPeerDeviceModule->InitPlayout(); mPeerDeviceModule->StartPlayout(); - mPeerDeviceModule->SetSpeakerMute(was_peer_mute); + mPeerDeviceModule->SetSpeakerMute(false); } mTuningDeviceModule->SetSpeakerMute(false); @@ -376,6 +402,8 @@ float LLWebRTCImpl::getPeerAudioLevel() { return 20 * mPeerAudioDeviceObserver-> void LLWebRTCImpl::setSpeakerVolume(float volume) { mPeerDeviceModule->SetSpeakerVolume( (uint32_t)(volume * VOLUME_SCALE_WEBRTC));} void LLWebRTCImpl::setMicrophoneVolume(float volume) { mPeerDeviceModule->SetMicrophoneVolume((uint32_t)(volume * VOLUME_SCALE_WEBRTC));} +void LLWebRTCImpl::setMute(bool mute) { mPeerDeviceModule->SetMicrophoneMute(mute); } + // // Helpers // @@ -566,9 +594,12 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp) mWebRTCImpl->PostSignalingTask( [this, sdp]() { - RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); - mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), - rtc::scoped_refptr(this)); + if (mPeerConnection) + { + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); + mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), + rtc::scoped_refptr(this)); + } }); } @@ -844,17 +875,6 @@ void LLWebRTCPeerConnectionImpl::OnSetLocalDescriptionComplete(webrtc::RTCError } -void LLWebRTCPeerConnectionImpl::setAudioObserver(LLWebRTCAudioObserver *observer) { mAudioObserverList.emplace_back(observer); } - -void LLWebRTCPeerConnectionImpl::unsetAudioObserver(LLWebRTCAudioObserver *observer) -{ - std::vector::iterator it = std::find(mAudioObserverList.begin(), mAudioObserverList.end(), observer); - if (it != mAudioObserverList.end()) - { - mAudioObserverList.erase(it); - } -} - // // DataChannelObserver implementation // @@ -897,9 +917,12 @@ void LLWebRTCPeerConnectionImpl::OnMessage(const webrtc::DataBuffer& buffer) void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary) { - rtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length()); - webrtc::DataBuffer buffer(cowBuffer, binary); - mDataChannel->Send(buffer); + 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); } diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 753fe6a983..ed80fa5648 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -88,19 +88,13 @@ class LLWebRTCDeviceInterface virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 virtual void setMicrophoneVolume(float volume) = 0; // volume between 0.0 and 1.0 + virtual void setMute(bool mute) = 0; virtual float getPeerAudioLevel() = 0; }; -class LLWebRTCAudioObserver -{ - public: -}; - class LLWebRTCAudioInterface { public: - virtual void setAudioObserver(LLWebRTCAudioObserver *observer) = 0; - virtual void unsetAudioObserver(LLWebRTCAudioObserver *observer) = 0; virtual void setMute(bool mute) = 0; }; diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index eb439b5a46..76e29c63fb 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -118,7 +118,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface float getPeerAudioLevel() override; void setSpeakerVolume(float volume) override; // range 0.0-1.0 - void setMicrophoneVolume(float volume) override; // range 0.0-1.0 + void setMicrophoneVolume(float volume) override; // range 0.0-1.0 + void setMute(bool mute) override; // // Helpers @@ -227,8 +228,6 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, // // LLWebRTCAudioInterface // - void setAudioObserver(LLWebRTCAudioObserver *observer) override; - void unsetAudioObserver(LLWebRTCAudioObserver *observer) override; void setMute(bool mute) override; // @@ -292,8 +291,6 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, bool mAnswerReceived; rtc::scoped_refptr mPeerConnection; - - std::vector mAudioObserverList; std::vector mDataObserverList; rtc::scoped_refptr mDataChannel; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 93efd2526d..31be3daed3 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -51,6 +51,7 @@ #include "llagent.h" #include "llcachename.h" #include "llimview.h" // for LLIMMgr +#include "llworld.h" #include "llparcel.h" #include "llviewerparcelmgr.h" #include "llfirstuse.h" @@ -230,7 +231,6 @@ LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr; LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mSessionTerminateRequested(false), mRelogRequested(false), - mTerminateDaemon(false), mSpatialJoiningNum(0), mTuningMode(false), @@ -247,11 +247,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mNextAudioSession(), mCurrentParcelLocalID(0), - mConnectorEstablished(false), - mAccountLoggedIn(false), - mNumberOfAliases(0), - mCommandCookie(0), - mLoginRetryCount(0), mBuddyListMapPopulated(false), mBlockRulesListReceived(false), @@ -271,7 +266,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mMicVolumeDirty(true), mVoiceEnabled(false), - mWriteInProgress(false), mLipSyncEnabled(false), @@ -280,17 +274,13 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mAvatarNameCacheConnection(), mIsInTuningMode(false), - mIsInChannel(false), mIsJoiningSession(false), mIsWaitingForFonts(false), mIsLoggingIn(false), - mIsLoggedIn(false), mIsProcessingChannels(false), mIsCoroutineActive(false), mWebRTCPump("WebRTCClientPump"), - mWebRTCDeviceInterface(nullptr), - mWebRTCPeerConnection(nullptr), - mWebRTCAudioInterface(nullptr) + mWebRTCDeviceInterface(nullptr) { sShuttingDown = false; sConnected = false; @@ -339,15 +329,12 @@ void LLWebRTCVoiceClient::init(LLPumpIO *pump) // constructor will set up LLVoiceClient::getInstance() sPump = pump; -// LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", -// boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, LLWebRTCVoiceClient::getInstance())); +// LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", +// boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); llwebrtc::init(); mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); mWebRTCDeviceInterface->setDevicesObserver(this); - - mWebRTCPeerConnection = llwebrtc::newPeerConnection(); - mWebRTCPeerConnection->setSignalingObserver(this); } void LLWebRTCVoiceClient::terminate() @@ -371,7 +358,7 @@ void LLWebRTCVoiceClient::cleanUp() { LL_DEBUGS("Voice") << LL_ENDL; - deleteAllSessions(); + sessionState::deleteAllSessions(); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -401,52 +388,46 @@ void LLWebRTCVoiceClient::updateSettings() ///////////////////////////// // session control messages -void LLWebRTCVoiceClient::connectorCreate() -{ +void LLWebRTCVoiceClient::predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) +{ + session->OnConnectionEstablished(channelID); } -void LLWebRTCVoiceClient::connectorShutdown() +void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID) { - - mShutdownComplete = true; + session->OnConnectionFailure(channelID); } -void LLWebRTCVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) { + sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); +} - mAccountDisplayName = user_id; - - LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; - - mAccountName = nameFromID(agentID); +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) +{ + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + mAudioSession = mNextAudioSession; + mNextAudioSession.reset(); + } + sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); } -void LLWebRTCVoiceClient::setLoginInfo( - const std::string& account_name, - const std::string& password, - const std::string& channel_sdp) +void LLWebRTCVoiceClient::sessionState::OnConnectionEstablished(const std::string& channelID) { - mRemoteChannelSDP = channel_sdp; - mWebRTCPeerConnection->AnswerAvailable(channel_sdp); + if (channelID == mPrimaryConnectionID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } +} - if(mAccountLoggedIn) - { - // Already logged in. - LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; - - // Don't process another login. - return; - } - else if ( account_name != mAccountName ) - { - LL_WARNS("Voice") << "Mismatched account name! " << account_name - << " instead of " << mAccountName << LL_ENDL; - } - else - { - mAccountPassword = password; - } +void LLWebRTCVoiceClient::sessionState::OnConnectionFailure(const std::string &channelID) +{ + if (channelID == mPrimaryConnectionID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } } void LLWebRTCVoiceClient::idle(void* user_data) @@ -460,1609 +441,449 @@ void LLWebRTCVoiceClient::idle(void* user_data) // // -typedef enum e_voice_control_coro_state -{ - VOICE_STATE_ERROR = -1, - VOICE_STATE_TP_WAIT = 0, // entry point - VOICE_STATE_START_DAEMON, - VOICE_STATE_PROVISION_ACCOUNT, - VOICE_STATE_SESSION_PROVISION_WAIT, - VOICE_STATE_START_SESSION, - VOICE_STATE_WAIT_FOR_SESSION_START, - VOICE_STATE_SESSION_RETRY, - VOICE_STATE_SESSION_ESTABLISHED, - VOICE_STATE_WAIT_FOR_CHANNEL, - VOICE_STATE_DISCONNECT, - VOICE_STATE_WAIT_FOR_EXIT, -} EVoiceControlCoroState; - -void LLWebRTCVoiceClient::voiceControlCoro() -{ - int state = 0; - try - { - // state is passed as a reference instead of being - // a member due to unresolved issues with coroutine - // surviving longer than LLWebRTCVoiceClient - voiceControlStateMachine(); - } - 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") << "voiceControlStateMachine crashed in state " << state << LL_ENDL; - throw; - } +void LLWebRTCVoiceClient::predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t& session) +{ + session->processSessionStates(); } -void LLWebRTCVoiceClient::voiceControlStateMachine() +void LLWebRTCVoiceClient::sessionState::processSessionStates() { - if (sShuttingDown) + std::map::iterator iter; + for (iter = mWebRTCConnections.begin(); iter != mWebRTCConnections.end();) { - return; + if (!iter->second->connectionStateMachine()) + { + iter = mWebRTCConnections.erase(iter); + } + else + { + ++iter; + } } +} +void LLWebRTCVoiceClient::voiceConnectionCoro() +{ LL_DEBUGS("Voice") << "starting" << LL_ENDL; mIsCoroutineActive = true; LLCoros::set_consuming(true); - - U32 retry = 0; - U32 provisionWaitTimeout = 0; - - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); - - do + try { - if (sShuttingDown) - { - // WebRTC singleton performed the exit, logged out, - // cleaned sockets, gateway and no longer cares - // about state of coroutine, so just stop - return; - } - - processIceUpdates(); - - switch (getVoiceControlState()) + while (!sShuttingDown) { - case VOICE_STATE_TP_WAIT: - // starting point for voice - if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) - { - LL_DEBUGS("Voice") << "Suspending voiceControlCoro() momentarily for teleport. Tuning: " << mTuningMode - << ". Relog: " << mRelogRequested << LL_ENDL; - llcoro::suspendUntilTimeout(1.0); - } - else - { - LLMutexLock lock(&mVoiceStateMutex); - - mTrickling = false; - mIceCompleted = false; - setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); - } - break; - - case VOICE_STATE_START_SESSION: - if (establishVoiceConnection() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) - { - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_SESSION_START, VOICE_STATE_SESSION_RETRY); - } - else - { - setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); - } - break; - - case VOICE_STATE_WAIT_FOR_SESSION_START: + // add session for region or parcel voice. + LLViewerRegion *regionp = gAgent.getRegion(); + if (!regionp) { - llcoro::suspendUntilTimeout(1.0); - std::string channel_sdp; - { - LLMutexLock lock(&mVoiceStateMutex); - if (mVoiceControlState == VOICE_STATE_SESSION_RETRY) - { - break; - } - if (!mChannelSDP.empty()) - { - mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; - } - } - break; + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + continue; } - case VOICE_STATE_PROVISION_ACCOUNT: - if (!provisionVoiceAccount()) - { - setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); - } - else - { - provisionWaitTimeout = 0; - setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); - } - break; - case VOICE_STATE_SESSION_PROVISION_WAIT: - llcoro::suspendUntilTimeout(1.0); - if (provisionWaitTimeout++ > PROVISION_WAIT_TIMEOUT_SEC) - { - setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); - } - break; - - case VOICE_STATE_SESSION_RETRY: - giveUp(); // cleans sockets and session - if (mRelogRequested) - { - // We failed to connect, give it a bit time before retrying. - retry++; - F32 full_delay = llmin(2.f * (F32) retry, 10.f); - F32 current_delay = 0.f; - LL_INFOS("Voice") << "Voice failed to establish session after " << retry << " tries. Will attempt to reconnect in " - << full_delay << " seconds" << LL_ENDL; - while (current_delay < full_delay && !sShuttingDown) - { - // Assuming that a second has passed is not accurate, - // but we don't need accurancy here, just to make sure - // that some time passed and not to outlive voice itself - current_delay++; - llcoro::suspendUntilTimeout(1.f); - } - } - setVoiceControlStateUnless(VOICE_STATE_DISCONNECT); - break; - - case VOICE_STATE_SESSION_ESTABLISHED: + if (regionp->getRegionID().isNull()) { - retry = 0; - if (mTuningMode) - { - performMicTuning(); - } - sessionEstablished(); - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_CHANNEL, VOICE_STATE_SESSION_RETRY); + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + continue; } - break; - - case VOICE_STATE_WAIT_FOR_CHANNEL: + if (!mAudioSession || mAudioSession->mIsSpatial) { - if ((!waitForChannel()) || !mVoiceEnabled) // todo: split into more states like login/fonts - { - setVoiceControlStateUnless(VOICE_STATE_DISCONNECT, VOICE_STATE_SESSION_RETRY); - } - // on true, it's a retry, so let the state stand. - } - break; - - case VOICE_STATE_DISCONNECT: - LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; - breakVoiceConnection(true); - retry = 0; // Connected without issues - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); - break; + // check to see if parcel changed. + std::string channelID = regionp->getRegionID().asString(); - case VOICE_STATE_WAIT_FOR_EXIT: - if (mVoiceEnabled) + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + S32 parcel_local_id = INVALID_PARCEL_ID; + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID && !parcel->getParcelFlagUseEstateVoiceChannel()) { - LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); + parcel_local_id = parcel->getLocalID(); + channelID += "-" + std::to_string(parcel->getLocalID()); } - else + if (!mAudioSession || channelID != mAudioSession->mChannelID) { - llcoro::suspendUntilTimeout(1.0); + setSpatialChannel(channelID, "", parcel_local_id); } - break; - - default: - { - LL_WARNS("Voice") << "Unknown voice control state " << getVoiceControlState() << LL_ENDL; - break; } + updatePosition(); + sessionState::for_each(boost::bind(predProcessSessionStates, _1)); + sendPositionAndVolumeUpdate(true); + updateOwnVolume(); + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); } - } while (true); -} - -bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) -{ - if (!sShuttingDown && mVoiceEnabled) + } + catch (const LLCoros::Stop&) { - LL_WARNS("Voice") << "SLVoice terminated " << ll_stream_notation_sd(data) << LL_ENDL; - terminateAudioSession(false); - closeSocket(); - cleanUp(); - LLVoiceClient::getInstance()->setUserPTTState(false); - gAgent.setVoiceConnected(false); - mRelogRequested = true; + LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL; } - return false; -} - -bool LLWebRTCVoiceClient::provisionVoiceAccount() -{ - LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; - - while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) + catch (const LLContinueError&) { - LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - // *TODO* Pump a message for wake up. - llcoro::suspend(); + LOG_UNHANDLED_EXCEPTION("LLWebRTCVoiceClient"); } - - if (sShuttingDown) + catch (...) { - return false; + // 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; } - std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); + cleanUp(); +} - LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; +bool LLWebRTCVoiceClient::performMicTuning() +{ + LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; - LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); - LLSD body; - LLSD jsep; - jsep["type"] = "offer"; - { - LLMutexLock lock(&mVoiceStateMutex); - jsep["sdp"] = mChannelSDP; - } - body["jsep"] = jsep; + mIsInTuningMode = false; - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( - url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisioned, this, _1), - boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure, this, url, 3, body, _1)); + //--------------------------------------------------------------------- return true; } -void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) -{ - LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); - std::string channelSDP; - if (result.has("jsep") && - result["jsep"].has("type") && - result["jsep"]["type"] == "answer" && - result["jsep"].has("sdp")) - { - channelSDP = result["jsep"]["sdp"].asString(); - } - std::string voiceAccountServerUri; - std::string voiceUserName = gAgent.getID().asString(); - std::string voicePassword = ""; // no password for now. - - LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" - << " user " << (voiceUserName.empty() ? "not set" : "set") << " password " - << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; +//========================================================================= - setLoginInfo(voiceUserName, voicePassword, channelSDP); +void LLWebRTCVoiceClient::sessionTerminate() +{ + mSessionTerminateRequested = true; +} - // switch to the default region channel. - switchChannel(gAgent.getRegion()->getRegionID().asString()); +void LLWebRTCVoiceClient::requestRelog() +{ + mSessionTerminateRequested = true; + mRelogRequested = true; } -void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) + +void LLWebRTCVoiceClient::leaveAudioSession() { - if (sShuttingDown) + if(mAudioSession) { - return; + LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mChannelID << LL_ENDL; } - if (retries >= 0) - { - - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( - url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisioned, this, _1), - boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure, this, url, retries - 1, body, _1)); - } - else - { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; - } + else + { + LL_WARNS("Voice") << "called with no active session" << LL_ENDL; + } + sessionTerminate(); } -bool LLWebRTCVoiceClient::establishVoiceConnection() +void LLWebRTCVoiceClient::clearCaptureDevices() { - LL_INFOS("Voice") << "Ice Gathering voice account." << LL_ENDL; - while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) - { - LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - // *TODO* Pump a message for wake up. - llcoro::suspend(); - return false; - } - - if (!mVoiceEnabled && mIsInitialized) - { - LL_WARNS("Voice") << "cannot establish connection; enabled "<initializeConnection(); + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mCaptureDevices.clear(); } -bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) +void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) { + LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; + mCaptureDevices.push_back(device); +} - LL_INFOS("Voice") << "Breaking voice account." << LL_ENDL; +LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices() +{ + return mCaptureDevices; +} - while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) +void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) +{ + bool inTuningMode = mIsInTuningMode; + if (inTuningMode) { - LL_DEBUGS("Voice") << "no capabilities for voice breaking; waiting " << LL_ENDL; - // *TODO* Pump a message for wake up. - llcoro::suspend(); - } - - if (sShuttingDown) + tuningStop(); + } + mWebRTCDeviceInterface->setCaptureDevice(name); + if (inTuningMode) { - return false; + tuningStart(); } - - std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); - - LL_DEBUGS("Voice") << "region ready for voice break; url=" << url << LL_ENDL; - - LL_DEBUGS("Voice") << "sending ProvisionVoiceAccountRequest (breaking) (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << 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; - httpAdapter->postAndSuspend(httpRequest, url, body); - mWebRTCPeerConnection->shutdownConnection(); - return true; } - -bool LLWebRTCVoiceClient::loginToWebRTC() +void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { - mRelogRequested = false; - mIsLoggedIn = true; - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - - // Set the initial state of mic mute, local speaker volume, etc. - sendLocalAudioUpdates(); - mIsLoggingIn = false; - - return true; + mDevicesListUpdated = state; } -void LLWebRTCVoiceClient::logoutOfWebRTC(bool wait) +void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) { - if (mIsLoggedIn) + clearRenderDevices(); + for (auto &device : render_devices) { - mAccountPassword.clear(); - breakVoiceConnection(wait); - // Ensure that we'll re-request provisioning before logging in again - mIsLoggedIn = false; + addRenderDevice(LLVoiceDevice(device.display_name, device.id)); + } + clearCaptureDevices(); + for (auto &device : capture_devices) + { + addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); } + setDevicesListUpdated(true); } -bool LLWebRTCVoiceClient::requestParcelVoiceInfo() -{ - //_INFOS("Voice") << "Requesting voice info for Parcel" << LL_ENDL; +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); +} - LLViewerRegion * region = gAgent.getRegion(); - if (region == NULL || !region->capabilitiesReceived()) +void LLWebRTCVoiceClient::tuningStart() +{ + if (!mIsInTuningMode) { - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL; - return false; + mWebRTCDeviceInterface->setTuningMode(true); + mIsInTuningMode = true; } +} - // grab the cap. - std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); - if (url.empty()) +void LLWebRTCVoiceClient::tuningStop() +{ + if (mIsInTuningMode) { - // Region dosn't have the cap. Stop probing. - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; - return false; + mWebRTCDeviceInterface->setTuningMode(false); + mIsInTuningMode = false; } - - // update the parcel - checkParcelChanged(true); - - LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << 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); +bool LLWebRTCVoiceClient::inTuningMode() +{ + return mIsInTuningMode; +} - LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD()); +void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) +{ + int scaled_volume = scale_mic_volume(volume); - if (sShuttingDown) - { - return false; - } + if(scaled_volume != mTuningMicVolume) + { + mTuningMicVolume = scaled_volume; + mTuningMicVolumeDirty = true; + } +} - LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; - LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); +void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) +{ - if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) - { - // if a terminate request has been received, - // bail and go to the stateSessionTerminated - // state. If the cap request is still pending, - // the responder will check to see if we've moved - // to a new session and won't change any state. - LL_DEBUGS("Voice") << "terminate requested " << mSessionTerminateRequested - << " enabled " << mVoiceEnabled - << " initialized " << mIsInitialized - << LL_ENDL; - terminateAudioSession(true); - return false; - } + if (volume != mTuningSpeakerVolume) + { + mTuningSpeakerVolume = volume; + mTuningSpeakerVolumeDirty = true; + } +} + +float LLWebRTCVoiceClient::tuningGetEnergy(void) +{ + return mWebRTCDeviceInterface->getTuningAudioLevel(); +} - if ((!status) || (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))) - { - if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) - { - LL_WARNS("Voice") << "Session terminated." << LL_ENDL; - } +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; +} - LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL; - sessionTerminate(); - return false; - } +void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) +{ + if(clearCurrentList) + { + clearCaptureDevices(); + clearRenderDevices(); + } + mWebRTCDeviceInterface->refreshDevices(); +} - std::string uri; - std::string credentials; +void LLWebRTCVoiceClient::giveUp() +{ + // All has failed. Clean up and stop trying. + LL_WARNS("Voice") << "Terminating Voice Service" << LL_ENDL; + cleanUp(); +} - LL_WARNS("Voice") << "Got voice credentials" << result << LL_ENDL; +void LLWebRTCVoiceClient::setHidden(bool hidden) +{ + mHidden = hidden; - if (result.has("voice_credentials")) + if (mHidden && inSpatialChannel()) { - 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; - } - } - } + // get out of the channel entirely + leaveAudioSession(); } else { - if (LLViewerParcelMgr::getInstance()->allowAgentVoice()) - { - LL_WARNS("Voice") << "No voice credentials" << LL_ENDL; - } - else - { - LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL; - } + sendPositionAndVolumeUpdate(true); } - - // set the spatial channel. If no voice credentials or uri are - // available, then we simply drop out of voice spatially. - return !setSpatialChannel(uri, ""); } -bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) +void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) { - mIsJoiningSession = true; + Json::FastWriter writer; + std::string spatial_data; + std::string volume_data; - sessionStatePtr_t oldSession = mAudioSession; - - LL_INFOS("Voice") << "Adding or joining voice session " << nextSession->mHandle << LL_ENDL; + F32 audio_level = 0.0; + uint32_t uint_audio_level = 0.0; - mAudioSession = nextSession; - mAudioSessionChanged = true; - if (!mAudioSession || !mAudioSession->mReconnect) + if (!mMuteMic) { - mNextAudioSession.reset(); + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); + uint_audio_level = (uint32_t) (audio_level * 128); + } - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); + if (mSpatialCoordsDirty || force) + { + Json::Value spatial = Json::objectValue; + LLVector3d earPosition; + LLQuaternion earRot; + switch (mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earRot = mCameraRot; + break; - llcoro::suspend(); + case earLocAvatar: + earPosition = mAvatarPosition; + earRot = mAvatarRot; + break; - if (sShuttingDown) - { - return false; - } + case earLocMixed: + earPosition = mAvatarPosition; + earRot = mCameraRot; + break; + } - LLSD result; + 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) (earPosition[0] * 100); + spatial["lp"]["y"] = (int) (earPosition[1] * 100); + spatial["lp"]["z"] = (int) (earPosition[2] * 100); + spatial["lh"] = Json::objectValue; + spatial["lh"]["x"] = (int) (earRot[0] * 100); + spatial["lh"]["y"] = (int) (earRot[1] * 100); + spatial["lh"]["z"] = (int) (earRot[2] * 100); + spatial["lh"]["w"] = (int) (earRot[3] * 100); - if (mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) - { - // Notify observers to let them know there is problem with voice - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL; + mSpatialCoordsDirty = false; + if (force || (uint_audio_level != mAudioLevel)) + { + spatial["p"] = uint_audio_level; + } + spatial_data = writer.write(spatial); } - - // 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) + if (force || (uint_audio_level != mAudioLevel)) { - mSpatialJoiningNum++; + Json::Value volume = Json::objectValue; + volume["p"] = uint_audio_level; + volume_data = writer.write(volume); } + mAudioLevel = uint_audio_level; - if (!mVoiceEnabled && mIsInitialized) - { - LL_DEBUGS("Voice") << "Voice no longer enabled. Exiting" - << " enabled " << mVoiceEnabled - << " initialized " << mIsInitialized - << LL_ENDL; - mIsJoiningSession = false; - // User bailed out during connect -- jump straight to teardown. - terminateAudioSession(true); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - return false; - } - else if (mSessionTerminateRequested) + sessionState::for_each(boost::bind(predSendData, _1, spatial_data, volume_data)); +} + +void LLWebRTCVoiceClient::updateOwnVolume() { + F32 audio_level = 0.0; + if (!mMuteMic) { - LL_DEBUGS("Voice") << "Terminate requested" << LL_ENDL; - if (mAudioSession && !mAudioSession->mHandle.empty()) - { - // Only allow direct exits from this state in p2p calls (for cancelling an invite). - // Terminating a half-connected session on other types of calls seems to break something in the WebRTC gateway. - if (mAudioSession->mIsP2P) - { - terminateAudioSession(true); - mIsJoiningSession = false; - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - return false; - } - } + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); } - 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. - // Just flush it all out and start new. - mWebRTCPump.discard(); - - // add 'self' participant. - addParticipantByID(gAgent.getID()); - // tell peers that this participant has joined. + sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); +} - if (mWebRTCDataInterface) +void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level) +{ + participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); + if (participant) { - Json::FastWriter writer; - Json::Value root = getPositionAndVolumeUpdateJson(true); - root["j"] = true; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + participant->mPower = audio_level; + participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; } - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - - return true; } -bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) +void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data, const std::string& volume_data) { - - if (mAudioSession) + if (session->mIsSpatial && !spatial_data.empty()) { - LL_INFOS("Voice") << "terminateAudioSession(" << wait << ") Terminating current voice session " << mAudioSession->mHandle << LL_ENDL; - - if (mIsLoggedIn) - { - if (!mAudioSession->mHandle.empty()) - { - if (wait) - { - LLSD result; - do - { - LLSD timeoutResult(LLSDMap("session", "timeout")); - - result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - - if (sShuttingDown) - { - return false; - } - - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - if (result.has("session")) - { - if (result.has("handle")) - { - if (result["handle"] != mAudioSession->mHandle) - { - LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; - continue; - } - } - - std::string message = result["session"].asString(); - if (message == "removed" || message == "timeout") - break; - } - } while (true); - - } - } - else - { - LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; - } - } - else - { - LL_WARNS("Voice") << "Session " << mAudioSession->mHandle << " already terminated by logout." << LL_ENDL; - } - - sessionStatePtr_t oldSession = mAudioSession; - - mAudioSession.reset(); - // We just notified status observers about this change. Don't do it again. - mAudioSessionChanged = false; - - // The old session may now need to be deleted. - reapSession(oldSession); + session->sendData(spatial_data); } - else + else if (!volume_data.empty()) { - LL_WARNS("Voice") << "terminateAudioSession(" << wait << ") with NULL mAudioSession" << LL_ENDL; + session->sendData(volume_data); } - - 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 - // the region chat. - mSessionTerminateRequested = false; - - bool status=((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !sShuttingDown); - LL_DEBUGS("Voice") << "exiting" - << " VoiceEnabled " << mVoiceEnabled - << " IsInitialized " << mIsInitialized - << " RelogRequested " << mRelogRequested - << " ShuttingDown " << (sShuttingDown ? "TRUE" : "FALSE") - << " returning " << status - << LL_ENDL; - return status; } - -typedef enum e_voice_wait_for_channel_state +void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) { - VOICE_CHANNEL_STATE_LOGIN = 0, // entry point - VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, - VOICE_CHANNEL_STATE_PROCESS_CHANNEL, - VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, - VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK -} EVoiceWaitForChannelState; + for (auto& connection : mWebRTCConnections) + { + connection.second->sendData(data); + } +} -bool LLWebRTCVoiceClient::waitForChannel() +void LLWebRTCVoiceClient::sendLocalAudioUpdates() { - LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL; - - EVoiceWaitForChannelState state = VOICE_CHANNEL_STATE_LOGIN; +} - do - { - if (sShuttingDown) - { - // terminate() forcefully disconects voice, no need for cleanup - return false; - } - if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) - { - mIsProcessingChannels = false; - return true; - } +void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) + { + sessionStatePtr_t oldSession = mAudioSession; - processIceUpdates(); - switch (state) - { - case VOICE_CHANNEL_STATE_LOGIN: - if (!loginToWebRTC()) - { - return false; - } - state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; - break; + mAudioSession = session; + mAudioSessionChanged = true; - case VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING: - mIsProcessingChannels = true; - llcoro::suspend(); - state = VOICE_CHANNEL_STATE_PROCESS_CHANNEL; - break; - - case VOICE_CHANNEL_STATE_PROCESS_CHANNEL: - if (mTuningMode) - { - performMicTuning(); - } - else if (checkParcelChanged() || (mNextAudioSession == NULL)) - { - // the parcel is changed, or we have no pending audio sessions, - // so try to request the parcel voice info - // if we have the cap, we move to the appropriate state - requestParcelVoiceInfo(); //suspends for http reply - } - else if (sessionNeedsRelog(mNextAudioSession)) - { - LL_INFOS("Voice") << "Session requesting reprovision and login." << LL_ENDL; - requestRelog(); - break; - } - else if (mNextAudioSession) - { - sessionStatePtr_t joinSession = mNextAudioSession; - mNextAudioSession.reset(); - if (!runSession(joinSession)) //suspends - { - LL_DEBUGS("Voice") << "runSession returned false; leaving inner loop" << LL_ENDL; - break; - } - else - { - LL_DEBUGS("Voice") - << "runSession returned true to inner loop" - << " RelogRequested=" << mRelogRequested - << " VoiceEnabled=" << mVoiceEnabled - << LL_ENDL; - } - } - - state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY; - break; - - case VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY: - if (!mNextAudioSession) - { - llcoro::suspendUntilTimeout(1.0); - } - state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK; - break; - - case VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK: - if (mVoiceEnabled && !mRelogRequested) - { - state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; - break; - } - else - { - mIsProcessingChannels = false; - LL_DEBUGS("Voice") - << "leaving inner waitForChannel loop" - << " RelogRequested=" << mRelogRequested - << " VoiceEnabled=" << mVoiceEnabled - << LL_ENDL; - return !sShuttingDown; - } - } - } while (true); -} - -bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) -{ - LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL; - - bool joined_session = addAndJoinSession(session); - - if (sShuttingDown) - { - return false; - } - - if (!joined_session) - { - notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - - if (mSessionTerminateRequested) - { - LL_DEBUGS("Voice") << "runSession terminate requested " << LL_ENDL; - terminateAudioSession(true); - } - // 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. - return !mRelogRequested; - } - - notifyParticipantObservers(); - - LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); - - mIsInChannel = true; - mMuteMicDirty = true; - - while (!sShuttingDown - && mVoiceEnabled - && !mSessionTerminateRequested - && !mTuningMode) - { - - if (sShuttingDown) - { - return false; - } - if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) - { - break; - } - - if (mSessionTerminateRequested) - { - break; - } - - if (mAudioSession && mAudioSession->mParticipantsChanged) - { - mAudioSession->mParticipantsChanged = false; - notifyParticipantObservers(); - } - - if (!inSpatialChannel()) - { - // When in a non-spatial channel, never send positional updates. - mSpatialCoordsDirty = false; - } - else - { - updatePosition(); - - if (checkParcelChanged()) - { - // *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 - // recheck next time around - if (requestParcelVoiceInfo()) // suspends - { // The parcel voice URI has changed.. break out and reconnect. - break; - } - - if (sShuttingDown) - { - return false; - } - } - // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) - enforceTether(); - } - sendPositionAndVolumeUpdate(); - - // send any requests to adjust mic and speaker settings if they have changed - sendLocalAudioUpdates(); - - mIsInitialized = true; - LLSD result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, UPDATE_THROTTLE_SECONDS, timeoutEvent); - - if (sShuttingDown) - { - return false; - } - - if (!result.has("timeout")) // logging the timeout event spams the log - { - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - } - if (result.has("session")) - { - if (result.has("handle")) - { - if (!mAudioSession) - { - LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while session is not initiated." << LL_ENDL; - continue; - } - if (result["handle"] != mAudioSession->mHandle) - { - LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; - continue; - } - } - - std::string message = result["session"]; - - if (message == "removed") - { - LL_DEBUGS("Voice") << "session removed" << LL_ENDL; - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - break; - } - } - else if (result.has("login")) - { - std::string message = result["login"]; - if (message == "account_logout") - { - LL_DEBUGS("Voice") << "logged out" << LL_ENDL; - mIsLoggedIn = false; - mRelogRequested = true; - break; - } - } - } - - if (sShuttingDown) - { - return false; - } - - mIsInChannel = false; - LL_DEBUGS("Voice") << "terminating at end of runSession" << LL_ENDL; - terminateAudioSession(true); - - return true; -} - -bool LLWebRTCVoiceClient::performMicTuning() -{ - LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; - - mIsInTuningMode = false; - - //--------------------------------------------------------------------- - return true; -} - -//========================================================================= - -void LLWebRTCVoiceClient::closeSocket(void) -{ - mSocket.reset(); - sConnected = false; - mConnectorEstablished = false; - mAccountLoggedIn = false; -} - -void LLWebRTCVoiceClient::logout() -{ - // Ensure that we'll re-request provisioning before logging in again - mAccountPassword.clear(); - mAccountLoggedIn = false; -} - -void LLWebRTCVoiceClient::sessionTerminate() -{ - mSessionTerminateRequested = true; -} - -void LLWebRTCVoiceClient::requestRelog() -{ - mSessionTerminateRequested = true; - mRelogRequested = true; -} - - -void LLWebRTCVoiceClient::leaveAudioSession() -{ - if(mAudioSession) - { - LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - - if(mAudioSession->mHandle.empty()) - { - LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; - } - } - else - { - LL_WARNS("Voice") << "called with no active session" << LL_ENDL; - } - sessionTerminate(); -} - -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) -{ - bool inTuningMode = mIsInTuningMode; - if (inTuningMode) - { - tuningStop(); - } - mWebRTCDeviceInterface->setCaptureDevice(name); - if (inTuningMode) - { - tuningStart(); - } -} -void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) -{ - mDevicesListUpdated = state; -} - -void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, - const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) -{ - clearRenderDevices(); - for (auto &device : render_devices) - { - addRenderDevice(LLVoiceDevice(device.display_name, device.id)); - } - clearCaptureDevices(); - for (auto &device : capture_devices) - { - addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); - } - 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) -{ - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mTuningMicVolume) - { - mTuningMicVolume = scaled_volume; - mTuningMicVolumeDirty = true; - } -} - -void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) -{ - - if (volume != mTuningSpeakerVolume) - { - mTuningSpeakerVolume = volume; - mTuningSpeakerVolumeDirty = true; - } -} - -float LLWebRTCVoiceClient::tuningGetEnergy(void) -{ - return mWebRTCDeviceInterface->getTuningAudioLevel(); -} - -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::daemonDied() -{ - // The daemon died, so the connection is gone. Reset everything and start over. - LL_WARNS("Voice") << "Connection to WebRTC daemon lost. Resetting state."<< LL_ENDL; - - //TODO: Try to relaunch the daemon -} - -void LLWebRTCVoiceClient::giveUp() -{ - // All has failed. Clean up and stop trying. - LL_WARNS("Voice") << "Terminating Voice Service" << LL_ENDL; - closeSocket(); - cleanUp(); -} - -void LLWebRTCVoiceClient::setHidden(bool hidden) -{ - mHidden = hidden; - - if (mHidden && inSpatialChannel()) - { - // get out of the channel entirely - leaveAudioSession(); - } - else - { - sendPositionAndVolumeUpdate(); - } -} - -Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) -{ - Json::Value root = Json::objectValue; - - if ((mSpatialCoordsDirty || force) && inSpatialChannel()) - { - LLVector3d earPosition; - LLQuaternion earRot; - switch (mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earRot = mAvatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earRot = mCameraRot; - break; - } - - root["sp"] = Json::objectValue; - root["sp"]["x"] = (int) (mAvatarPosition[0] * 100); - root["sp"]["y"] = (int) (mAvatarPosition[1] * 100); - root["sp"]["z"] = (int) (mAvatarPosition[2] * 100); - root["sh"] = Json::objectValue; - root["sh"]["x"] = (int) (mAvatarRot[0] * 100); - root["sh"]["y"] = (int) (mAvatarRot[1] * 100); - root["sh"]["z"] = (int) (mAvatarRot[2] * 100); - root["sh"]["w"] = (int) (mAvatarRot[3] * 100); - - root["lp"] = Json::objectValue; - root["lp"]["x"] = (int) (earPosition[0] * 100); - root["lp"]["y"] = (int) (earPosition[1] * 100); - root["lp"]["z"] = (int) (earPosition[2] * 100); - root["lh"] = Json::objectValue; - root["lh"]["x"] = (int) (earRot[0] * 100); - root["lh"]["y"] = (int) (earRot[1] * 100); - root["lh"]["z"] = (int) (earRot[2] * 100); - root["lh"]["w"] = (int) (earRot[3] * 100); - - mSpatialCoordsDirty = false; - } - - F32 audio_level = 0.0; - - if (!mMuteMic) - { - audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); - } - uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); - if (force || (uint_audio_level != mAudioLevel)) - { - root["p"] = uint_audio_level; - mAudioLevel = uint_audio_level; - participantStatePtr_t participant = findParticipantByID(gAgentID); - if (participant) - { - participant->mPower = audio_level; - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; - } - } - return root; -} - -void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate() -{ - - - if (mWebRTCDataInterface && mWebRTCAudioInterface) - { - Json::Value root = getPositionAndVolumeUpdateJson(false); - - if (root.size() > 0) - { - - Json::FastWriter writer; - std::string json_data = writer.write(root); - - mWebRTCDataInterface->sendData(json_data, false); - } - } - - - 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 - if(!p->mIsSelf) - { - // scale from the range 0.0-1.0 to WebRTC volume in the range 0-100 - S32 volume = ll_round(p->mVolume / VOLUME_SCALE_WEBRTC); - bool mute = p->mOnMuteList; - - if(mute) - { - // SetParticipantMuteForMe doesn't work in p2p sessions. - // If we want the user to be muted, set their volume to 0 as well. - // This isn't perfect, but it will at least reduce their volume to a minimum. - volume = 0; - // Mark the current volume level as set to prevent incoming events - // 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; - } - - p->mVolumeDirty = false; - } - } - } -} - -void LLWebRTCVoiceClient::sendLocalAudioUpdates() -{ -} - -///////////////////////////// -// WebRTC Signaling Handlers -void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) -{ - LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - - switch (state) - { - case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE: - { - LLMutexLock lock(&mVoiceStateMutex); - mIceCompleted = true; - break; - } - case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW: - { - LLMutexLock lock(&mVoiceStateMutex); - mIceCompleted = false; - } - default: - break; - } -} - -void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) -{ - LLMutexLock lock(&mVoiceStateMutex); - mIceCandidates.push_back(candidate); -} - -void LLWebRTCVoiceClient::processIceUpdates() -{ - LL_INFOS("Voice") << "Ice Gathering voice account." << LL_ENDL; - while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) - { - LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - // *TODO* Pump a message for wake up. - llcoro::suspend(); - } - - if (sShuttingDown) - { - return; - } - - std::string url = gAgent.getRegionCapability("VoiceSignalingRequest"); - - LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL; - - 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); - - bool iceCompleted = false; - LLSD body; - { - LLMutexLock lock(&mVoiceStateMutex); - - if (!mTrickling) - { - if (mIceCandidates.size()) - { - LLSD candidates = LLSD::emptyArray(); - for (auto &ice_candidate : mIceCandidates) - { - LLSD body_candidate; - body_candidate["sdpMid"] = ice_candidate.sdp_mid; - body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; - body_candidate["candidate"] = ice_candidate.candidate; - 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; - } - else - { - return; - } - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( - url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, iceCompleted, _1), - boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); - mTrickling = true; - } - } -} - -void LLWebRTCVoiceClient::onIceUpdateComplete(bool ice_completed, const LLSD& result) -{ mTrickling = false; } - -void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result) -{ - if (sShuttingDown) - { - 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(&LLWebRTCVoiceClient::onIceUpdateComplete, this, ice_completed, _1), - boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); - } - else - { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; - setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); - mTrickling = false; - } -} - -void LLWebRTCVoiceClient::OnOfferAvailable(const std::string &sdp) -{ - LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; - LLMutexLock lock(&mVoiceStateMutex); - mChannelSDP = sdp; -} - -void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * audio_interface) -{ - LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; - mWebRTCAudioInterface = audio_interface; - mWebRTCAudioInterface->setAudioObserver(this); - float speaker_volume = 0; - { - LLMutexLock lock(&mVoiceStateMutex); - speaker_volume = mSpeakerVolume; - } - mWebRTCDeviceInterface->setSpeakerVolume(mSpeakerVolume); - setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); -} - -void LLWebRTCVoiceClient::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' - join - object of join data (TBD) (true for now) - // 'l' - boolean, always true if exists. - - 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; - 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; - } - participantStatePtr_t participant = findParticipantByID(agent_id); - bool joined = voice_data[participant_id].get("j", Json::Value(false)).asBool(); - new_participant |= joined; - if (!participant && joined) - { - participant = addParticipantByID(agent_id); - } - if (participant) - { - if(voice_data[participant_id].get("l", Json::Value(false)).asBool()) - { - removeParticipantByID(agent_id); - } - F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 128; - // convert to decibles - participant->mPower = energyRMS; - /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we - can use it at some point when we actually process frames. */ - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; - } - } - } -} - -void LLWebRTCVoiceClient::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) -{ - mWebRTCDataInterface = data_interface; - mWebRTCDataInterface->setDataObserver(this); -} - - -void LLWebRTCVoiceClient::OnRenegotiationNeeded() -{ - LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; - mRelogRequested = TRUE; - mIsProcessingChannels = FALSE; - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); - setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); -} - -void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) -{ - LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; - if(mAudioSession != session) - { - sessionStatePtr_t oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(mIsJoiningSession) - { - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) - ("session", "joined")); + // The old session may now need to be deleted. + reapSession(oldSession); + } + + // This is the session we're joining. + if(mIsJoiningSession) + { + LLSD WebRTCevent(LLSDMap("channel", session->mChannelID) + ("session", "joined")); mWebRTCPump.post(WebRTCevent); if(!session->mIsChannel) { // this is a p2p session. Make sure the other end is added as a participant. - participantStatePtr_t participant(session->addParticipant(LLUUID(session->mSIPURI))); + participantStatePtr_t participant(session->addParticipant(LLUUID(session->mChannelID))); if(participant) { if(participant->mAvatarIDValid) @@ -2076,8 +897,7 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) } // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? - LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + LL_INFOS("Voice") << "added caller as participant (" << participant->mAvatarID << ")"<< LL_ENDL; } } } @@ -2086,20 +906,19 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) - { - + { if(session == mAudioSession) { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; + LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the current session)" << LL_ENDL; } else if(session == mNextAudioSession) { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; + LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the next session)" << LL_ENDL; } else { // We don't have a reason to keep tracking this session, so just delete it. - LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; + LL_DEBUGS("Voice") << "deleting session " << session->mChannelID << LL_ENDL; deleteSession(session); } } @@ -2109,35 +928,12 @@ void LLWebRTCVoiceClient::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 LLWebRTCVoiceClient::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); - } - } - } - - return result; -} void LLWebRTCVoiceClient::leftAudioSession(const sessionStatePtr_t &session) { if (mAudioSession == session) { - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) + LLSD WebRTCevent(LLSDMap("channel", session->mChannelID) ("session", "removed")); mWebRTCPump.post(WebRTCevent); @@ -2293,17 +1089,20 @@ void LLWebRTCVoiceClient::sessionState::removeAllParticipants() /*static*/ void LLWebRTCVoiceClient::sessionState::VerifySessions() { - std::set::iterator it = mSession.begin(); - while (it != mSession.end()) + return; + /* + std::map::iterator it = mSessions.begin(); + while (it != mSessions.end()) { - if ((*it).expired()) + if ((*it).second.expired()) { LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; - it = mSession.erase(it); + it = mSessions.erase(it); } else ++it; } + */ } @@ -2336,16 +1135,6 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::fi participantMap::iterator iter = mParticipantsByURI.find(uri); - if(iter == mParticipantsByURI.end()) - { - if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) - { - // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. - // Look up the other URI - iter = mParticipantsByURI.find(mSIPURI); - } - } - if(iter != mParticipantsByURI.end()) { result = iter->second; @@ -2367,37 +1156,40 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::fi return result; } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const LLUUID& id) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string& channelID, const LLUUID& id) { participantStatePtr_t result; + auto& session = sessionState::matchSessionByChannelID(channelID); - if(mAudioSession) + if (session) { - result = mAudioSession->findParticipantByID(id); + result = session->findParticipantByID(id); } return result; } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const LLUUID &id) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string& channelID, const LLUUID &id) { participantStatePtr_t result; - if (mAudioSession) + auto& session = sessionState::matchSessionByChannelID(channelID); + if (session) { - result = mAudioSession->addParticipant(id); + result = session->addParticipant(id); } return result; } -void LLWebRTCVoiceClient::removeParticipantByID(const LLUUID &id) +void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id) { participantStatePtr_t result; - if (mAudioSession) + auto& session = sessionState::matchSessionByChannelID(channelID); + if (session) { - participantStatePtr_t participant = mAudioSession->findParticipantByID(id); + participantStatePtr_t participant = session->findParticipantByID(id); if (participant) { - mAudioSession->removeParticipant(participant); + session->removeParticipant(participant); } } } @@ -2437,28 +1229,29 @@ bool LLWebRTCVoiceClient::checkParcelChanged(bool update) } bool LLWebRTCVoiceClient::switchChannel( - std::string uri, + const std::string channelID, bool spatial, bool no_reconnect, bool is_p2p, - std::string hash) + std::string hash, + S32 parcel_local_id) { - bool needsSwitch = !mIsInChannel; - - if (mIsInChannel) + bool needsSwitch = false; + + if (mAudioSession) { if (mSessionTerminateRequested) { // 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->mChannelID != channelID) needsSwitch = true; } else { // mNextAudioSession is null -- this probably means we're on our way back to spatial. - if(!uri.empty()) + if (!channelID.empty()) { // We do want to process a switch in this case. needsSwitch = true; @@ -2470,14 +1263,14 @@ bool LLWebRTCVoiceClient::switchChannel( // Otherwise, compare against the URI we're in now. if(mAudioSession) { - if(mAudioSession->mSIPURI != uri) + if (mAudioSession->mChannelID != channelID) { needsSwitch = true; } } else { - if(!uri.empty()) + if (!channelID.empty()) { // mAudioSession is null -- it's not clear what case would cause this. // For now, log it as a warning and see if it ever crops up. @@ -2487,10 +1280,24 @@ bool LLWebRTCVoiceClient::switchChannel( } } } + else + { + if (!mNextAudioSession || mNextAudioSession->mChannelID != channelID) + { + needsSwitch = true; + } + } if(needsSwitch) { - if(uri.empty()) + if (mAudioSession) + { + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + mAudioSession->shutdownAllConnections(); + } + if (channelID.empty()) { // Leave any channel we may be in LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; @@ -2511,20 +1318,13 @@ bool LLWebRTCVoiceClient::switchChannel( } else { - LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; + LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; - mNextAudioSession = addSession(uri); + mNextAudioSession = addSession(channelID, parcel_local_id); mNextAudioSession->mIsSpatial = spatial; mNextAudioSession->mReconnect = !no_reconnect; mNextAudioSession->mIsP2P = is_p2p; } - - if (mIsInChannel) - { - // If we're already in a channel, or if we're joining one, terminate - // so we can rejoin with the new session data. - sessionTerminate(); - } } return needsSwitch; @@ -2534,7 +1334,7 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) { mNextAudioSession = session; - if (mIsInChannel) + if (mAudioSession) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. @@ -2550,14 +1350,13 @@ void LLWebRTCVoiceClient::setNonSpatialChannel( } bool LLWebRTCVoiceClient::setSpatialChannel( - const std::string &uri, const std::string &credentials) + const std::string &uri, const std::string &credentials, S32 parcel_local_id) { - mSpatialSessionURI = uri; - mAreaVoiceDisabled = mSpatialSessionURI.empty(); + mAreaVoiceDisabled = uri.empty(); LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - if((mIsInChannel && mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) + if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) { // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; @@ -2565,31 +1364,29 @@ bool LLWebRTCVoiceClient::setSpatialChannel( } else { - return switchChannel(mSpatialSessionURI, true, false, false); + return switchChannel(uri, true, false, false, "", parcel_local_id); } } void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) -{ - switchChannel(uuid.asString(), false, true, true); +{ + switchChannel(uuid.asString(), false, true, true); } - - void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) { } -bool LLWebRTCVoiceClient::isValidChannel(std::string &sessionHandle) +bool LLWebRTCVoiceClient::isValidChannel(std::string &channelID) { - return(findSession(sessionHandle) != NULL); + return(findP2PSession(LLUUID(channelID)) != NULL); } -bool LLWebRTCVoiceClient::answerInvite(std::string &sessionHandle) +bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) { // this is only ever used to answer incoming p2p call invites. - sessionStatePtr_t session(findSession(sessionHandle)); + sessionStatePtr_t session(findP2PSession(LLUUID(channelID))); if(session) { session->mIsSpatial = false; @@ -2618,21 +1415,6 @@ bool LLWebRTCVoiceClient::isVoiceWorking() const BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) { BOOL result = TRUE; - sessionStatePtr_t session(findSession(id)); - - if(!session) - { - // Didn't find a matching session -- check the current audio session for a matching participant - if(mAudioSession) - { - participantStatePtr_t participant(findParticipantByID(id)); - if(participant) - { - result = participant->isAvatar(); - } - } - } - return result; } @@ -2641,7 +1423,7 @@ BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) { BOOL result = TRUE; - sessionStatePtr_t session(findSession(session_id)); + sessionStatePtr_t session(findP2PSession(session_id)); if(session != NULL) { @@ -2656,7 +1438,7 @@ BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) { bool result = TRUE; - sessionStatePtr_t session(findSession(session_id)); + sessionStatePtr_t session(findP2PSession(session_id)); if(session != NULL) { @@ -2689,26 +1471,12 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel() std::string LLWebRTCVoiceClient::getCurrentChannel() { - std::string result; - - if (mIsInChannel && !mSessionTerminateRequested) - { - result = getAudioSessionURI(); - } - - return result; + return getAudioSessionURI(); } bool LLWebRTCVoiceClient::inProximalChannel() { - bool result = false; - - if (mIsInChannel && !mSessionTerminateRequested) - { - result = inSpatialChannel(); - } - - return result; + return inSpatialChannel(); } std::string LLWebRTCVoiceClient::nameFromAvatar(LLVOAvatar *avatar) @@ -2817,22 +1585,11 @@ std::string LLWebRTCVoiceClient::getAudioSessionURI() std::string result; if(mAudioSession) - result = mAudioSession->mSIPURI; - - return result; -} - -std::string LLWebRTCVoiceClient::getAudioSessionHandle() -{ - std::string result; - - if(mAudioSession) - result = mAudioSession->mHandle; + result = mAudioSession->mChannelID; return result; } - ///////////////////////////// // Sending updates of current state @@ -2877,7 +1634,7 @@ void LLWebRTCVoiceClient::updatePosition(void) LLWebRTCVoiceClient::getInstance()->setCameraPosition( pos, // position LLVector3::zero, // velocity - LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix + LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix // Send the current avatar position to the voice code qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); @@ -2891,6 +1648,8 @@ void LLWebRTCVoiceClient::updatePosition(void) pos, // position LLVector3::zero, // velocity qrot); // rotation matrix + + enforceTether(); } } @@ -2953,7 +1712,7 @@ bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string void LLWebRTCVoiceClient::leaveChannel(void) { - if (mIsInChannel) + if (mAudioSession || mNextAudioSession) { LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; mChannelName.clear(); @@ -2963,16 +1722,22 @@ void LLWebRTCVoiceClient::leaveChannel(void) void LLWebRTCVoiceClient::setMuteMic(bool muted) { - participantStatePtr_t participant = findParticipantByID(gAgentID); + + if (mWebRTCDeviceInterface) + { + mWebRTCDeviceInterface->setMute(muted); + } + mMuteMic = muted; + sessionState::for_each(boost::bind(predSetMuteMic, _1, muted)); +} + +void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool muted) +{ + participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); if (participant) - { + { participant->mPower = 0.0; - } - if (mWebRTCAudioInterface) - { - mWebRTCAudioInterface->setMute(muted); - } - mMuteMic = muted; + } } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -2998,8 +1763,8 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) if (!mIsCoroutineActive) { - LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", - boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, LLWebRTCVoiceClient::getInstance())); + LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", + boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); } else { @@ -3064,7 +1829,6 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) if (volume != mSpeakerVolume) { { - LLMutexLock lock(&mVoiceStateMutex); int min_volume = 0.0; if ((volume == min_volume) || (mSpeakerVolume == min_volume)) { @@ -3074,7 +1838,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) mSpeakerVolume = volume; mSpeakerVolumeDirty = true; } - if (mWebRTCAudioInterface) + if (mWebRTCDeviceInterface) { mWebRTCDeviceInterface->setSpeakerVolume(volume); } @@ -3097,7 +1861,11 @@ void LLWebRTCVoiceClient::setMicGain(F32 volume) BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) { BOOL result = FALSE; - participantStatePtr_t participant(findParticipantByID(id)); + if (!mAudioSession) + { + return FALSE; + } + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { // I'm not sure what the semantics of this should be. @@ -3111,7 +1879,11 @@ BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) { std::string result; - participantStatePtr_t participant(findParticipantByID(id)); + if (!mAudioSession) + { + return result; + } + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { result = participant->mDisplayName; @@ -3125,8 +1897,11 @@ std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; - - participantStatePtr_t participant(findParticipantByID(id)); + if (!mAudioSession) + { + return result; + } + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { result = participant->mIsSpeaking; @@ -3138,8 +1913,11 @@ BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) { BOOL result = FALSE; - - participantStatePtr_t participant(findParticipantByID(id)); + if (!mAudioSession) + { + return result; + } + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { result = participant->mIsModeratorMuted; @@ -3151,7 +1929,11 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { F32 result = 0; - participantStatePtr_t participant(findParticipantByID(id)); + if (!mAudioSession) + { + return result; + } + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if (participant) { result = participant->mPower * 2.0; @@ -3162,8 +1944,11 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id) { BOOL result = FALSE; - - participantStatePtr_t participant(findParticipantByID(id)); + if (!mAudioSession) + { + return result; + } + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { // I'm not sure what the semantics of this should be. @@ -3178,7 +1963,7 @@ BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id) { BOOL result = FALSE; - participantStatePtr_t participant(findParticipantByID(id)); + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { result = participant->mOnMuteList; @@ -3193,7 +1978,7 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) // Minimum volume will be returned for users with voice disabled F32 result = LLVoiceClient::VOLUME_MIN; - participantStatePtr_t participant(findParticipantByID(id)); + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { result = participant->mVolume; @@ -3209,7 +1994,7 @@ void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { if(mAudioSession) { - participantStatePtr_t participant(findParticipantByID(id)); + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if (participant && !participant->mIsSelf) { if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) @@ -3235,7 +2020,7 @@ std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) { std::string result; - participantStatePtr_t participant(findParticipantByID(id)); + participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if(participant) { result = participant->mGroupID; @@ -3250,12 +2035,11 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() } //------------------------------------------------------------------------ -std::set LLWebRTCVoiceClient::sessionState::mSession; +std::map LLWebRTCVoiceClient::sessionState::mSessions; LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), - mMediaStreamState(streamStateUnknown), mIsChannel(false), mIsSpatial(false), mIsP2P(false), @@ -3269,23 +2053,26 @@ LLWebRTCVoiceClient::sessionState::sessionState() : } /*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession() +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession(const std::string& channelID, S32 parcelLocalID) { - sessionState::ptr_t ptr(new sessionState()); + LLUUID region_id = gAgent.getRegion()->getRegionID(); + + sessionState::ptr_t session(new sessionState()); + session->mChannelID = channelID; + session->mWebRTCConnections[channelID] = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); + session->mPrimaryConnectionID = channelID; - std::pair::iterator, bool> result = mSession.insert(ptr); + // add agent as participant + session->addParticipant(gAgentID); - if (result.second) - ptr->mMyIterator = result.first; + mSessions[channelID] = session; - return ptr; + return session; } LLWebRTCVoiceClient::sessionState::~sessionState() { - LL_INFOS("Voice") << "Destroying session handle=" << mHandle << " SIP=" << mSIPURI << LL_ENDL; - if (mMyIterator != mSession.end()) - mSession.erase(mMyIterator); + LL_INFOS("Voice") << "Destroying session CHANNEL=" << mChannelID << LL_ENDL; removeAllParticipants(); } @@ -3304,78 +2091,49 @@ bool LLWebRTCVoiceClient::sessionState::isTextIMPossible() return false; } - -/*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByHandle(const std::string &handle) -{ - sessionStatePtr_t result; - - // *TODO: My kingdom for a lambda! - std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByHandle, _1, handle)); - - if (it != mSession.end()) - result = (*it).lock(); - - return result; -} - -/*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByURI(const std::string &uri) -{ - sessionStatePtr_t result; - - // *TODO: My kingdom for a lambda! - std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testBySIPOrAlterateURI, _1, uri)); - - if (it != mSession.end()) - result = (*it).lock(); - - return result; -} - /*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByParticipant(const LLUUID &participant_id) +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByChannelID(const std::string& channel_id) { sessionStatePtr_t result; // *TODO: My kingdom for a lambda! - std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCallerId, _1, participant_id)); - - if (it != mSession.end()) - result = (*it).lock(); - + std::map::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(mSession.begin(), mSession.end(), boost::bind(for_eachPredicate, _1, func)); + std::for_each(mSessions.begin(), mSessions.end(), boost::bind(for_eachPredicate, _1, func)); } -// simple test predicates. -// *TODO: These should be made into lambdas when we can pull the trigger on newer C++ features. -bool LLWebRTCVoiceClient::sessionState::testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle) +void LLWebRTCVoiceClient::sessionState::reapEmptySessions() { - ptr_t aLock(a.lock()); - - return aLock ? aLock->mHandle == handle : false; + std::map::iterator iter; + for (iter = mSessions.begin(); iter != mSessions.end();) + { + if (!iter->second->isEmpty()) + { + iter = mSessions.erase(iter); + } + else + { + ++iter; + } + } } -bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) -{ - ptr_t aLock(a.lock()); - - return aLock ? (aLock->mSIPURI == uri) : false; -} -bool LLWebRTCVoiceClient::sessionState::testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) +bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) { ptr_t aLock(a.lock()); - return aLock ? ((aLock->mSIPURI == uri) || (aLock->mAlternateSIPURI == uri)) : false; + return aLock ? (aLock->mChannelID == LLUUID(uri)) : false; } - bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId) { ptr_t aLock(a.lock()); @@ -3384,9 +2142,9 @@ bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient } /*static*/ -void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceClient::sessionState::wptr_t &a, sessionFunc_t func) +void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pair &a, sessionFunc_t func) { - ptr_t aLock(a.lock()); + ptr_t aLock(a.second.lock()); if (aLock) func(aLock); @@ -3396,425 +2154,807 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceCli } } -void LLWebRTCVoiceClient::sessionEstablished() -{ - mWebRTCAudioInterface->setMute(mMuteMic); - addSession(gAgent.getRegion()->getRegionID().asString()); -} - -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const std::string &handle) +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const LLUUID &agent_id) { - sessionStatePtr_t result; - sessionMap::iterator iter = mSessionsByHandle.find(handle); - if(iter != mSessionsByHandle.end()) - { - result = iter->second; - } + sessionStatePtr_t result = sessionState::matchSessionByChannelID(agent_id.asString()); + if (result && result->mIsP2P) + { + return result; + } + result.reset(); return result; } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const LLUUID &participant_id) -{ - sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); - - return result; + + +void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() +{ + for (auto &&connection : mWebRTCConnections) + { + connection.second->shutDown(); + } } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &uri, const std::string &handle) + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string& channel_id, S32 parcel_local_id) { sessionStatePtr_t result; - - if(handle.empty()) - { - // No handle supplied. - // Check whether there's already a session with this URI - result = sessionState::matchSessionByURI(uri); - } - else // (!handle.empty()) - { - // Check for an existing session with this handle - sessionMap::iterator iter = mSessionsByHandle.find(handle); - - if(iter != mSessionsByHandle.end()) - { - result = iter->second; - } - } + + // Check whether there's already a session with this URI + result = sessionState::matchSessionByChannelID(channel_id); if(!result) { // No existing session found. - LL_DEBUGS("Voice") << "adding new session: handle \"" << handle << "\" URI " << uri << LL_ENDL; - result = sessionState::createSession(); - result->mSIPURI = uri; - result->mHandle = handle; + LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; + result = sessionState::createSession(channel_id, parcel_local_id); if (LLVoiceClient::instance().getVoiceEffectEnabled()) { result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); } - - 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 - // a mAuidoSession that does not match the session tracked in mSessionsByHandle - mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); - } } else { // Found an existing session - if(uri != result->mSIPURI) + if (channel_id != result->mChannelID) { // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; - setSessionURI(result, uri); + LL_DEBUGS("Voice") << "changing uri from " << result->mChannelID << " to " << channel_id << LL_ENDL; + + result->mChannelID = channel_id; + + verifySessionState(); } + + LL_DEBUGS("Voice") << "returning existing session: CHANNEL " << channel_id << LL_ENDL; + } + + verifySessionState(); + + return result; +} + +void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) +{ + // At this point, the session should be unhooked from all lists and all state should be consistent. + verifySessionState(); + session->shutdownAllConnections(); + // If this is the current audio session, clean up the pointer which will soon be dangling. + if(mAudioSession == session) + { + mAudioSession.reset(); + mAudioSessionChanged = true; + } + + // ditto for the next audio session + if(mNextAudioSession == session) + { + mNextAudioSession.reset(); + } +} + +void LLWebRTCVoiceClient::sessionState::deleteAllSessions() +{ + mSessions.clear(); +} + +void LLWebRTCVoiceClient::verifySessionState(void) +{ + sessionState::VerifySessions(); +} + +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) << " )" + << " mAudioSession=" << mAudioSession + << LL_ENDL; - if(handle != result->mHandle) + if(mAudioSession) + { + if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) { - if(handle.empty()) + switch(mAudioSession->mErrorStatusCode) { - // There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. - LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; + case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; + case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; + case 20715: + //invalid channel, we may be using a set of poorly cached + //info + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + case 1009: + //invalid username and password + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; } - else + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + } + else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) + { + switch(mAudioSession->mErrorStatusCode) { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; - setSessionHandle(result, handle); + case HTTP_NOT_FOUND: // NOT_FOUND + // *TODO: Should this be 503? + case 480: // TEMPORARILY_UNAVAILABLE + case HTTP_REQUEST_TIME_OUT: // REQUEST_TIMEOUT + // call failed because other user was not available + // treat this as an error case + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + break; } } + } - LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; + LL_DEBUGS("Voice") + << " " << LLVoiceClientStatusObserver::status2string(status) + << ", session URI " << getAudioSessionURI() + << ", proximal is " << inSpatialChannel() + << LL_ENDL; + + for (status_observer_set_t::iterator it = mStatusObservers.begin(); + it != mStatusObservers.end(); + ) + { + LLVoiceClientStatusObserver* observer = *it; + observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + // In case onError() deleted an entry. + it = mStatusObservers.upper_bound(observer); + } + mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN; + { + + } + // 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) +{ + mFriendObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) +{ + mFriendObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyFriendObservers() +{ + for (friend_observer_set_t::iterator it = mFriendObservers.begin(); + it != mFriendObservers.end(); + ) + { + LLFriendObserver* observer = *it; + it++; + // The only friend-related thing we notify on is online/offline transitions. + observer->changed(LLFriendObserver::ONLINE); + } +} + +void LLWebRTCVoiceClient::lookupName(const LLUUID &id) +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); } + mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); +} - verifySessionState(); - - return result; +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::clearSessionHandle(const sessionStatePtr_t &session) +void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) { - if (session) + participantStatePtr_t participant(session->findParticipantByID(id)); + if (participant) { - if (!session->mHandle.empty()) - { - sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); - if (iter != mSessionsByHandle.end()) - { - mSessionsByHandle.erase(iter); - } - } - else - { - LL_WARNS("Voice") << "Session has empty handle!" << LL_ENDL; - } + // Found -- fill in the name + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; } - else + + // Check whether this is a p2p session whose caller name just resolved + if (session->mCallerID == id) { - LL_WARNS("Voice") << "Attempt to clear NULL session!" << LL_ENDL; + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; } - } -void LLWebRTCVoiceClient::setSessionHandle(const sessionStatePtr_t &session, const std::string &handle) +void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) { - // 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. - sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); - if(iter != mSessionsByHandle.end()) - { - if(iter->second != session) - { - LL_WARNS("Voice") << "Internal error: session mismatch! Session may have been duplicated. Removing version in map." << LL_ENDL; - } + sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); +} - mSessionsByHandle.erase(iter); - } - else - { - LL_WARNS("Voice") << "Attempt to remove session with handle " << session->mHandle << " not found in map!" << LL_ENDL; - } - } - - session->mHandle = handle; - if(!handle.empty()) - { - mSessionsByHandle.insert(sessionMap::value_type(session->mHandle, session)); - } +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } - verifySessionState(); -} -void LLWebRTCVoiceClient::setSessionURI(const sessionStatePtr_t &session, const std::string &uri) +///////////////////////////// +// WebRTC Signaling Handlers + +LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string& channelID) : + mWebRTCPeerConnection(nullptr), + mWebRTCAudioInterface(nullptr), + mWebRTCDataInterface(nullptr), + mIceCompleted(false), + mTrickling(false), + mVoiceConnectionState(VOICE_STATE_START_SESSION), + mChannelID(channelID), + mRegionID(regionID), + mParcelLocalID(parcelLocalID), + mShutDown(false) { - // There used to be a map of session URIs to sessions, which made this complex.... - session->mSIPURI = uri; + mWebRTCPeerConnection = llwebrtc::newPeerConnection(); + mWebRTCPeerConnection->setSignalingObserver(this); +} - verifySessionState(); +LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + // peer connection and observers will be cleaned up + // by llwebrtc::terminate() on shutdown. + return; + } + mWebRTCPeerConnection->unsetSignalingObserver(this); + llwebrtc::freePeerConnection(mWebRTCPeerConnection); + mWebRTCPeerConnection = nullptr; } -void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) +void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) { - // Remove the session from the handle map - if(!session->mHandle.empty()) - { - sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); - if(iter != mSessionsByHandle.end()) - { - if(iter->second != session) - { - LL_WARNS("Voice") << "Internal error: session mismatch, removing session in map." << LL_ENDL; - } - mSessionsByHandle.erase(iter); - } - } + LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - // At this point, the session should be unhooked from all lists and all state should be consistent. - verifySessionState(); + switch (state) + { + case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE: + { + LLMutexLock lock(&mVoiceStateMutex); + mIceCompleted = true; + break; + } + case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW: + { + LLMutexLock lock(&mVoiceStateMutex); + mIceCompleted = false; + } + default: + break; + } +} - // If this is the current audio session, clean up the pointer which will soon be dangling. - if(mAudioSession == session) - { - mAudioSession.reset(); - mAudioSessionChanged = true; - } +void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +{ + LLMutexLock lock(&mVoiceStateMutex); + mIceCandidates.push_back(candidate); +} - // ditto for the next audio session - if(mNextAudioSession == session) - { - mNextAudioSession.reset(); - } +void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) +{ + 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()) + { + 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)); + } + else + { + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + mTrickling = false; + } } -void LLWebRTCVoiceClient::deleteAllSessions() +void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { - LL_DEBUGS("Voice") << LL_ENDL; + LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; + LLMutexLock lock(&mVoiceStateMutex); + mChannelSDP = sdp; + if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START) + { + mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION; + } +} - while (!mSessionsByHandle.empty()) - { - const sessionStatePtr_t session = mSessionsByHandle.begin()->second; - deleteSession(session); - } - +void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +{ + LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; + mWebRTCAudioInterface = audio_interface; + setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); } -void LLWebRTCVoiceClient::verifySessionState(void) +void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binary) { - LL_DEBUGS("Voice") << "Sessions in handle map=" << mSessionsByHandle.size() << LL_ENDL; - sessionState::VerifySessions(); + // 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' - join - object of join data (TBD) (true for now) + // 'l' - boolean, always true if exists. + + 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; + 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 = voice_data[participant_id].get("j", Json::Value(false)).asBool(); + new_participant |= joined; + if (!participant && joined) + { + participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); + } + if (participant) + { + if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) + { + LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); + } + else + { + F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 128; + // convert to decibles + participant->mPower = energyRMS; + /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we + can use it at some point when we actually process frames. */ + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; + } + } + } + } } -void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { - mParticipantObservers.insert(observer); + if (data_interface) + { + mWebRTCDataInterface = data_interface; + mWebRTCDataInterface->setDataObserver(this); + Json::FastWriter writer; + Json::Value root = Json::objectValue; + root["j"] = true; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); + } } -void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { - mParticipantObservers.erase(observer); + LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } -void LLWebRTCVoiceClient::notifyParticipantObservers() +void LLVoiceWebRTCConnection::processIceUpdates() { - 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); - } + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + 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; + + 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); + + bool iceCompleted = false; + LLSD body; + { + if (!mTrickling) + { + if (mIceCandidates.size()) + { + LLSD candidates = LLSD::emptyArray(); + for (auto &ice_candidate : mIceCandidates) + { + LLSD body_candidate; + body_candidate["sdpMid"] = ice_candidate.sdp_mid; + body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; + body_candidate["candidate"] = ice_candidate.candidate; + 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; + } + else + { + return; + } + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), + boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); + mTrickling = true; + } + } } -void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) +bool LLVoiceWebRTCConnection::requestVoiceConnection() { - mStatusObservers.insert(observer); -} + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); -void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) -{ - mStatusObservers.erase(observer); -} + LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; + if (!regionp || !regionp->capabilitiesReceived()) + { + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + return false; + } -void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) -{ - LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" - << " mAudioSession=" << mAudioSession - << LL_ENDL; + std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); + if (url.empty()) + { + return false; + } - if(mAudioSession) - { - if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) - { - switch(mAudioSession->mErrorStatusCode) - { - case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; - case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; - case 20715: - //invalid channel, we may be using a set of poorly cached - //info - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - case 1009: - //invalid username and password - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - } + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - } - else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) - { - switch(mAudioSession->mErrorStatusCode) - { - case HTTP_NOT_FOUND: // NOT_FOUND - // *TODO: Should this be 503? - case 480: // TEMPORARILY_UNAVAILABLE - case HTTP_REQUEST_TIME_OUT: // REQUEST_TIMEOUT - // call failed because other user was not available - // treat this as an error case - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + 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; + } - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - break; - } - } - } - - LL_DEBUGS("Voice") - << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session URI " << getAudioSessionURI() - << ", proximal is " << inSpatialChannel() - << LL_ENDL; + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + return true; +} - for (status_observer_set_t::iterator it = mStatusObservers.begin(); - it != mStatusObservers.end(); - ) - { - LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); - // In case onError() deleted an entry. - it = mStatusObservers.upper_bound(observer); - } +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); - // 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(); + if (result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) + { + mRemoteChannelSDP = result["jsep"]["sdp"].asString(); + } + std::string voiceAccountServerUri; + std::string voiceUserName = gAgent.getID().asString(); + std::string voicePassword = ""; // no password for now. - gAgent.setVoiceConnected(voice_status); + LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" + << " user " << (voiceUserName.empty() ? "not set" : "set") << " password " + << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << mRemoteChannelSDP << LL_ENDL; - if (voice_status) - { - LLFirstUse::speak(true); - } - } + mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); } -void LLWebRTCVoiceClient::addObserver(LLFriendObserver* observer) +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { - mFriendObservers.insert(observer); + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + if (retries >= 0) + { + 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)); + } + else + { + LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } } -void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) +bool LLVoiceWebRTCConnection::connectionStateMachine() { - mFriendObservers.erase(observer); -} + U32 retry = 0; + + processIceUpdates(); -void LLWebRTCVoiceClient::notifyFriendObservers() -{ - for (friend_observer_set_t::iterator it = mFriendObservers.begin(); - it != mFriendObservers.end(); - ) - { - LLFriendObserver* observer = *it; - it++; - // The only friend-related thing we notify on is online/offline transitions. - observer->changed(LLFriendObserver::ONLINE); - } -} + switch (getVoiceConnectionState()) + { + case VOICE_STATE_START_SESSION: + { + mTrickling = false; + mIceCompleted = false; + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_SESSION_START); + if (!mWebRTCPeerConnection->initializeConnection()) + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } + break; + } + case VOICE_STATE_WAIT_FOR_SESSION_START: + { + break; + } + case VOICE_STATE_REQUEST_CONNECTION: + if (!requestVoiceConnection()) + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } + else + { + setVoiceConnectionState(VOICE_STATE_CONNECTION_WAIT); + } + break; + case VOICE_STATE_CONNECTION_WAIT: + break; -void LLWebRTCVoiceClient::lookupName(const LLUUID &id) -{ - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); + case VOICE_STATE_SESSION_ESTABLISHED: + { + LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID); + setVoiceConnectionState(VOICE_STATE_SESSION_UP); + } + break; + case VOICE_STATE_SESSION_UP: + { + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + } + break; + } + + case VOICE_STATE_SESSION_RETRY: + LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID); + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + break; + break; + + case VOICE_STATE_DISCONNECT: + if (breakVoiceConnection(true)) + { + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + } + retry = 0; // Connected without issues + break; + + case VOICE_STATE_WAIT_FOR_EXIT: + break; + case VOICE_STATE_SESSION_EXIT: + { + { + LLMutexLock lock(&mVoiceStateMutex); + if (!mShutDown) + { + mVoiceConnectionState = VOICE_STATE_START_SESSION; + } + else + { + return false; + } + } + break; + } + + default: + { + LL_WARNS("Voice") << "Unknown voice control state " << getVoiceConnectionState() << LL_ENDL; + return false; + } + } + return true; } -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 LLVoiceWebRTCConnection::sendData(const std::string& data) { + if (mWebRTCDataInterface) + { + mWebRTCDataInterface->sendData(data, false); + } } -void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) +bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) { - participantStatePtr_t participant(session->findParticipantByID(id)); - if (participant) + LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; + if (mWebRTCDataInterface) { - // Found -- fill in the name - participant->mAccountName = name; - // and post a "participants updated" message to listeners later. - session->mParticipantsChanged = true; + mWebRTCDataInterface->unsetDataObserver(this); + mWebRTCDataInterface = nullptr; + } + mWebRTCAudioInterface = nullptr; + if (mWebRTCPeerConnection) + { + mWebRTCPeerConnection->shutdownConnection(); + } + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + if (!regionp || !regionp->capabilitiesReceived()) + { + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + return false; } - // Check whether this is a p2p session whose caller name just resolved - if (session->mCallerID == id) + std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); + if (url.empty()) { - // this session's "caller ID" just resolved. Fill in the name. - session->mName = name; + return false; } -} -void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) -{ - sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); -} + 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); -std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } + LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); + LLSD body; + body["logout"] = TRUE; + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + return true; +} -LLWebRTCSecurity::LLWebRTCSecurity() +void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) { - // This size is an arbitrary choice; WebRTC does not care - // Use a multiple of three so that there is no '=' padding in the base64 (purely an esthetic choice) - #define WebRTC_TOKEN_BYTES 9 - U8 random_value[WebRTC_TOKEN_BYTES]; - - for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) - { - random_value[b] = ll_rand() & 0xff; - } - mConnectorHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); - - for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) + if (LLWebRTCVoiceClient::isShuttingDown()) { - random_value[b] = ll_rand() & 0xff; + return; } - mAccountHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } -LLWebRTCSecurity::~LLWebRTCSecurity() +void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { -} + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + if (retries >= 0) + { + 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)); + } + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); +} \ No newline at end of file diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ee7edb3030..456681ed25 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -39,6 +39,7 @@ class LLWebRTCProtocolParser; #include "llcallingcard.h" // for LLFriendObserver #include "lleventcoro.h" #include "llcoros.h" +#include "llparcel.h" #include #include "json/reader.h" @@ -53,14 +54,13 @@ class LLWebRTCProtocolParser; #include class LLAvatarName; +class LLVoiceWebRTCConnection; +typedef boost::shared_ptr connectionPtr_t; class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface, - public llwebrtc::LLWebRTCDevicesObserver, - public llwebrtc::LLWebRTCSignalingObserver, - public llwebrtc::LLWebRTCAudioObserver, - public llwebrtc::LLWebRTCDataObserver + public llwebrtc::LLWebRTCDevicesObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); @@ -72,6 +72,8 @@ public: //@{ 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; @@ -145,8 +147,12 @@ public: void setNonSpatialChannel(const std::string &uri, const std::string &credentials) override; - bool setSpatialChannel(const std::string &uri, - const std::string &credentials) override; + bool setSpatialChannel(const std::string &uri, const std::string &credentials) override + { + return setSpatialChannel(uri, credentials, INVALID_PARCEL_ID); + } + + bool setSpatialChannel(const std::string &uri, const std::string &credentials, S32 localParcelID); void leaveNonSpatialChannel() override; @@ -163,9 +169,9 @@ public: //@{ // start a voice channel with the specified user void callUser(const LLUUID &uuid) override; - bool isValidChannel(std::string &channelHandle) override; - bool answerInvite(std::string &channelHandle) override; - void declineInvite(std::string &channelHandle) override; + bool isValidChannel(std::string &channelID) override; + bool answerInvite(std::string &channelID) override; + void declineInvite(std::string &channelID) override; //@} ///////////////////////// @@ -232,10 +238,14 @@ public: bool isPreviewPlaying() override { return false; } //@} - // authorize the user - void userAuthorized(const std::string& user_id, - const LLUUID &agentID) override; + void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {}; + + + void OnConnectionEstablished(const std::string& channelID); + void OnConnectionFailure(const std::string &channelID); + void sendPositionAndVolumeUpdate(bool force); + void updateOwnVolume(); ////////////////////////////// /// @name Status notification @@ -247,10 +257,6 @@ public: void addObserver(LLVoiceClientParticipantObserver* observer) override; void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - - //@} - - ////////////////////////////// /// @name Devices change notification @@ -260,64 +266,17 @@ public: const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override; //@} - ////////////////////////////// - /// @name Signaling notification - // LLWebRTCSignalingObserver - //@{ - void OnIceGatheringState(IceGatheringState 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; - //@} - - ////////////////////////////// - /// @name Signaling notification - // LLWebRTCAudioObserver - //@{ - //@} - - ///////////////////////// - /// @name Data Notification - /// LLWebRTCDataObserver - //@{ - void OnDataReceived(const std::string& data, bool binary) override; - void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; - //@} - - void processIceUpdates(); - void onIceUpdateComplete(bool ice_completed, const LLSD& result); - void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result); - - - //@} - -protected: - ////////////////////// - // WebRTC Specific definitions - - - enum streamState - { - streamStateUnknown = 0, - streamStateIdle = 1, - streamStateConnected = 2, - streamStateRinging = 3, - streamStateConnecting = 6, // same as WebRTC session_media_connecting enum - streamStateDisconnecting = 7, //Same as WebRTC session_media_disconnecting enum - }; struct participantState { public: participantState(const LLUUID& agent_id); - bool updateMuteState(); // true if mute state has changed + bool updateMuteState(); // true if mute state has changed bool isAvatar(); std::string mURI; LLUUID mAvatarID; - std::string mAccountName; std::string mDisplayName; LLFrameTimer mSpeakingTimeout; F32 mLastSpokeTimestamp; @@ -337,6 +296,12 @@ protected: typedef boost::shared_ptr participantStatePtr_t; typedef boost::weak_ptr participantStateWptr_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 participantMap; typedef std::map participantUUIDMap; @@ -348,7 +313,7 @@ protected: typedef boost::function sessionFunc_t; - static ptr_t createSession(); + static ptr_t createSession(const std::string& channelID, S32 parcel_local_id); ~sessionState(); participantStatePtr_t addParticipant(const LLUUID& agent_id); @@ -358,28 +323,37 @@ protected: participantStatePtr_t findParticipant(const std::string &uri); participantStatePtr_t findParticipantByID(const LLUUID& id); - static ptr_t matchSessionByHandle(const std::string &handle); - static ptr_t matchSessionByURI(const std::string &uri); - static ptr_t matchSessionByParticipant(const LLUUID &participant_id); + static ptr_t matchSessionByChannelID(const std::string& channel_id); + + void shutdownAllConnections(); bool isCallBackPossible(); bool isTextIMPossible(); + + void processSessionStates(); + + void OnConnectionEstablished(const std::string &channelID); + void OnConnectionFailure(const std::string &channelID); + + void sendData(const std::string &data); static void for_each(sessionFunc_t func); + static void reapEmptySessions(); + + bool isEmpty() { return mWebRTCConnections.empty(); } + std::string mHandle; std::string mGroupHandle; - std::string mSIPURI; + std::string mChannelID; std::string mAlias; std::string mName; - std::string mAlternateSIPURI; std::string mErrorStatusString; std::queue mTextMsgQueue; LLUUID mIMSessionID; LLUUID mCallerID; int mErrorStatusCode; - int mMediaStreamState; bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) bool mIsSpatial; // True for spatial channels bool mIsP2P; @@ -399,18 +373,20 @@ protected: LLUUID mVoiceFontID; static void VerifySessions(); + static void deleteAllSessions(); private: + + std::map mWebRTCConnections; + std::string mPrimaryConnectionID; + sessionState(); - static std::set mSession; // canonical list of outstanding sessions. - std::set::iterator mMyIterator; // used for delete + static std::map mSessions; // canonical list of outstanding sessions. - static void for_eachPredicate(const wptr_t &a, sessionFunc_t func); + static void for_eachPredicate(const std::pair &a, sessionFunc_t func); - static bool testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle); static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); - static bool testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); }; @@ -422,28 +398,20 @@ protected: // Private Member Functions ////////////////////////////////////////////////////// - - + static void predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t &session); + static void predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); + static void predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); + static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); + static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); + static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute); ////////////////////////////// /// @name TVC/Server management and communication //@{ - // 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 connectorCreate(); - void connectorShutdown(); - void closeSocket(void); - // void requestVoiceAccountProvision(S32 retries = 3); - void setLoginInfo( - const std::string& account_name, - const std::string& password, - const std::string& channel_sdp); - void logout(); //@} @@ -486,30 +454,12 @@ protected: ///////////////////////////// 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. - - participantStatePtr_t findParticipantByID(const LLUUID& id); - participantStatePtr_t addParticipantByID(const LLUUID &id); - void removeParticipantByID(const LLUUID &id); -#if 0 - //////////////////////////////////////// - // voice sessions. - typedef std::set sessionSet; - - typedef sessionSet::iterator sessionIterator; - sessionIterator sessionsBegin(void); - sessionIterator sessionsEnd(void); -#endif - void sessionEstablished(); - sessionStatePtr_t findSession(const std::string &handle); - 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); - void setSessionURI(const sessionStatePtr_t &session, const std::string &uri); + void sessionEstablished(const LLUUID& region_id); + sessionStatePtr_t findP2PSession(const LLUUID &agent_id); + + sessionStatePtr_t addSession(const std::string& channel_id, S32 parcel_local_id); void deleteSession(const sessionStatePtr_t &session); - void deleteAllSessions(void); void verifySessionState(void); @@ -518,11 +468,7 @@ 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); - + void reapSession(const sessionStatePtr_t &session); ////////////////////////////////////// // buddy list stuff, needed for SLIM later @@ -583,41 +529,9 @@ private: // Coroutine support methods //--- - void voiceControlCoro(); - void voiceControlStateMachine(); - - int mVoiceControlState; - LLMutex mVoiceStateMutex; - void setVoiceControlStateUnless(int new_voice_control_state, int unless=-1) - { - LLMutexLock lock(&mVoiceStateMutex); - if (mVoiceControlState != unless) - { - mVoiceControlState = new_voice_control_state; - } - } - int getVoiceControlState() - { - LLMutexLock lock(&mVoiceStateMutex); - return mVoiceControlState; - } - - bool callbackEndDaemon(const LLSD& data); - bool provisionVoiceAccount(); - void OnVoiceAccountProvisioned(const LLSD& body); - void OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result); - bool establishVoiceConnection(); - bool breakVoiceConnection(bool wait); - bool loginToWebRTC(); - void logoutOfWebRTC(bool wait); - - bool requestParcelVoiceInfo(); + void voiceConnectionCoro(); - bool addAndJoinSession(const sessionStatePtr_t &nextSession); - bool terminateAudioSession(bool wait); - - bool waitForChannel(); - bool runSession(const sessionStatePtr_t &session); + void voiceConnectionStateMachine(); bool performMicTuning(); //--- @@ -632,72 +546,38 @@ private: 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 - bool mTerminateDaemon; - - std::string mAccountName; - std::string mAccountPassword; - std::string mChannelSDP; - std::string mRemoteChannelSDP; - std::string mAccountDisplayName; - bool mTuningMode; float mTuningEnergy; - std::string mTuningAudioFile; int mTuningMicVolume; bool mTuningMicVolumeDirty; int mTuningSpeakerVolume; 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; - sessionStatePtr_t mAudioSession; // Session state for the current audio session + 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. sessionStatePtr_t mNextAudioSession; // Session state for the audio session we're trying to join 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 - int mNumberOfAliases; - U32 mCommandCookie; - - 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; llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; - llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; - llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; - llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; LLVoiceDeviceList mCaptureDevices; LLVoiceDeviceList mRenderDevices; - std::vector mIceCandidates; - bool mIceCompleted; - bool mTrickling; uint32_t mAudioLevel; @@ -705,7 +585,12 @@ private: 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 = ""); + bool switchChannel(const std::string channelID, + bool spatial = true, + bool no_reconnect = false, + bool is_p2p = false, + std::string hash = "", + S32 parcel_local_id = INVALID_PARCEL_ID); void joinSession(const sessionStatePtr_t &session); std::string nameFromAvatar(LLVOAvatar *avatar); @@ -716,11 +601,8 @@ private: bool inSpatialChannel(void); std::string getAudioSessionURI(); - std::string getAudioSessionHandle(); void setHidden(bool hidden) override; //virtual - Json::Value getPositionAndVolumeUpdateJson(bool force); - void sendPositionAndVolumeUpdate(); void enforceTether(void); @@ -759,9 +641,6 @@ private: bool mMicVolumeDirty; bool mVoiceEnabled; - bool mWriteInProgress; - std::string mWriteString; - size_t mWriteOffset; BOOL mLipSyncEnabled; @@ -781,15 +660,13 @@ private: S32 mPlayRequestCount; bool mIsInTuningMode; - bool mIsInChannel; bool mIsJoiningSession; bool mIsWaitingForFonts; bool mIsLoggingIn; - bool mIsLoggedIn; bool mIsProcessingChannels; bool mIsCoroutineActive; - // This variables can last longer than WebRTC in coroutines so we need them as static + // These variables can last longer than WebRTC in coroutines so we need them as static static bool sShuttingDown; static bool sConnected; static LLPumpIO* sPump; @@ -797,19 +674,6 @@ private: LLEventMailDrop mWebRTCPump; }; -class LLWebRTCSecurity : public LLSingleton -{ - LLSINGLETON(LLWebRTCSecurity); - virtual ~LLWebRTCSecurity(); - - public: - std::string connectorHandle() { return mConnectorHandle; }; - std::string accountHandle() { return mAccountHandle; }; - - private: - std::string mConnectorHandle; - std::string mAccountHandle; -}; class LLVoiceWebRTCStats : public LLSingleton { @@ -843,5 +707,119 @@ class LLVoiceWebRTCStats : public LLSingleton LLSD read(); }; +class LLVoiceWebRTCConnection : + public llwebrtc::LLWebRTCSignalingObserver, + public llwebrtc::LLWebRTCDataObserver +{ + public: + LLVoiceWebRTCConnection(const LLUUID& regionID, S32 parcelLocalID, const std::string& channelID); + + virtual ~LLVoiceWebRTCConnection(); + + ////////////////////////////// + /// @name Signaling notification + // LLWebRTCSignalingObserver + //@{ + void OnIceGatheringState(IceGatheringState 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; + //@} + + ///////////////////////// + /// @name Data Notification + /// LLWebRTCDataObserver + //@{ + void OnDataReceived(const std::string &data, bool binary) override; + void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; + //@} + + void processIceUpdates(); + void onIceUpdateComplete(bool ice_completed, const LLSD &result); + void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); + + bool requestVoiceConnection(); + void OnVoiceConnectionRequestSuccess(const LLSD &body); + void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + + bool connectionStateMachine(); + + void sendData(const std::string &data); + + void shutDown() + { + LLMutexLock lock(&mVoiceStateMutex); + mShutDown = true; + } + +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_SESSION_UP = 0x20, + VOICE_STATE_SESSION_RETRY = 0x40, + VOICE_STATE_DISCONNECT = 0x80, + VOICE_STATE_WAIT_FOR_EXIT = 0x100, + VOICE_STATE_SESSION_EXIT = 0x200, + VOICE_STATE_SESSION_STOPPING = 0x3c0, + VOICE_STATE_SESSION_WAITING = 0x10a + } 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() + { + LLMutexLock lock(&mVoiceStateMutex); + return mVoiceConnectionState; + } + + bool breakVoiceConnection(bool wait); + void OnVoiceDisconnectionRequestSuccess(const LLSD &body); + void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + + std::string mChannelSDP; + std::string mRemoteChannelSDP; + + std::string mChannelID; + LLUUID mRegionID; + S32 mParcelLocalID; + + bool mShutDown; + + std::vector mIceCandidates; + bool mIceCompleted; + bool mTrickling; + + llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; +}; + + #endif //LL_WebRTC_VOICE_CLIENT_H -- cgit v1.2.3 From 7bc2f93e53105c043814c5c9bb1bac80e57b15aa Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 30 Nov 2023 14:37:17 -0800 Subject: fix mac build issue --- indra/newview/llvoicewebrtc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 31be3daed3..26b617bde4 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1159,7 +1159,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::fi LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string& channelID, const LLUUID& id) { participantStatePtr_t result; - auto& session = sessionState::matchSessionByChannelID(channelID); + auto session = sessionState::matchSessionByChannelID(channelID); if (session) { @@ -1172,7 +1172,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantB LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string& channelID, const LLUUID &id) { participantStatePtr_t result; - auto& session = sessionState::matchSessionByChannelID(channelID); + auto session = sessionState::matchSessionByChannelID(channelID); if (session) { result = session->addParticipant(id); @@ -1183,7 +1183,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantBy void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id) { participantStatePtr_t result; - auto& session = sessionState::matchSessionByChannelID(channelID); + auto session = sessionState::matchSessionByChannelID(channelID); if (session) { participantStatePtr_t participant = session->findParticipantByID(id); @@ -2957,4 +2957,4 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); -} \ No newline at end of file +} -- cgit v1.2.3 From 0b9c27dc70255385fd65ac2f1f8f99e2e013ea03 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 1 Dec 2023 01:14:33 -0800 Subject: Using the device module to set speaker/mic volume set the system mic/volume for all applications. Instead, modify the volume on the various streams. --- indra/llwebrtc/llwebrtc.cpp | 56 +++++++++++++++++++------ indra/llwebrtc/llwebrtc.h | 6 +-- indra/llwebrtc/llwebrtc_impl.h | 10 ++--- indra/newview/llvoicewebrtc.cpp | 92 ++++++++++++++++++++++++++++++++++++----- indra/newview/llvoicewebrtc.h | 18 ++++++++ 5 files changed, 148 insertions(+), 34 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index b2f5e0212e..4741ed92a3 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -399,11 +399,6 @@ float LLWebRTCImpl::getTuningAudioLevel() { return 20 * mTuningAudioDeviceObserv float LLWebRTCImpl::getPeerAudioLevel() { return 20 * mPeerAudioDeviceObserver->getMicrophoneEnergy(); } -void LLWebRTCImpl::setSpeakerVolume(float volume) { mPeerDeviceModule->SetSpeakerVolume( (uint32_t)(volume * VOLUME_SCALE_WEBRTC));} -void LLWebRTCImpl::setMicrophoneVolume(float volume) { mPeerDeviceModule->SetMicrophoneVolume((uint32_t)(volume * VOLUME_SCALE_WEBRTC));} - -void LLWebRTCImpl::setMute(bool mute) { mPeerDeviceModule->SetMicrophoneMute(mute); } - // // Helpers // @@ -417,6 +412,7 @@ LLWebRTCPeerConnection * LLWebRTCImpl::newPeerConnection() peerConnection->enableTracks(!mMute); return peerConnection.get(); } + void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnection * peer_connection) { std::vector>::iterator it = @@ -506,12 +502,11 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() audioOptions.echo_cancellation = false; // incompatible with opus stereo audioOptions.noise_suppression = true; - rtc::scoped_refptr stream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); - + mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); rtc::scoped_refptr audio_track( mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(audioOptions).get())); audio_track->set_enabled(true); - stream->AddTrack(audio_track); + mLocalStream->AddTrack(audio_track); mPeerConnection->AddTrack(audio_track, {"SLStream"}); @@ -545,6 +540,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() params.codecs.push_back(codecparam); receiver->SetParameters(params); } + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions; mPeerConnection->CreateOffer(this, offerOptions); @@ -606,13 +602,47 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp) void LLWebRTCPeerConnectionImpl::setMute(bool mute) { mMute = mute; - auto senders = mPeerConnection->GetSenders(); - - RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); + if (mPeerConnection) + { + auto senders = mPeerConnection->GetSenders(); + + RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); + for (auto &sender : senders) + { + auto track = sender->track(); + if(track) + { + track->set_enabled(!mMute); + } + } + } +} + +void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume) +{ + auto receivers = mPeerConnection->GetReceivers(); - for (auto &sender : senders) + for (auto &receiver : receivers) { - sender->track()->set_enabled(!mMute); + for (auto& stream : receiver->streams()) + { + for (auto& track : stream->GetAudioTracks()) + { + track->GetSource()->SetVolume(volume); + } + } + } +} + +void LLWebRTCPeerConnectionImpl::setSendVolume(float volume) +{ + if (mLocalStream) + { + auto track = mLocalStream->FindAudioTrack("SLStream"); + if (track) + { + track->GetSource()->SetVolume(volume); + } } } diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index ed80fa5648..9766f2f231 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -85,10 +85,6 @@ class LLWebRTCDeviceInterface virtual void setTuningMode(bool enable) = 0; virtual float getTuningAudioLevel() = 0; - - virtual void setSpeakerVolume(float volume) = 0; // volume between 0.0 and 1.0 - virtual void setMicrophoneVolume(float volume) = 0; // volume between 0.0 and 1.0 - virtual void setMute(bool mute) = 0; virtual float getPeerAudioLevel() = 0; }; @@ -96,6 +92,8 @@ 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 }; class LLWebRTCDataObserver diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 76e29c63fb..efb0d3add3 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -116,10 +116,6 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface void setTuningMode(bool enable) override; float getTuningAudioLevel() override; float getPeerAudioLevel() override; - - void setSpeakerVolume(float volume) override; // range 0.0-1.0 - void setMicrophoneVolume(float volume) override; // range 0.0-1.0 - void setMute(bool mute) override; // // Helpers @@ -224,12 +220,13 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, void 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 // @@ -291,6 +288,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, bool mAnswerReceived; rtc::scoped_refptr mPeerConnection; + rtc::scoped_refptr mLocalStream; std::vector mDataObserverList; rtc::scoped_refptr mDataChannel; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 26b617bde4..435e2e1245 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -746,7 +746,7 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) if (!mMuteMic) { - audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel() * mMicVolume; uint_audio_level = (uint32_t) (audio_level * 128); } @@ -853,6 +853,33 @@ void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) } } +void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) +{ + mMuted = muted; + for (auto& connection : mWebRTCConnections) + { + connection.second->setMuteMic(muted); + } +} + +void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) +{ + mMicGain = gain; + for (auto& connection : mWebRTCConnections) + { + connection.second->setMicGain(gain); + } +} + +void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) +{ + mSpeakerVolume = volume; + for (auto& connection : mWebRTCConnections) + { + connection.second->setSpeakerVolume(volume); + } +} + void LLWebRTCVoiceClient::sendLocalAudioUpdates() { } @@ -1723,10 +1750,6 @@ void LLWebRTCVoiceClient::leaveChannel(void) void LLWebRTCVoiceClient::setMuteMic(bool muted) { - if (mWebRTCDeviceInterface) - { - mWebRTCDeviceInterface->setMute(muted); - } mMuteMic = muted; sessionState::for_each(boost::bind(predSetMuteMic, _1, muted)); } @@ -1738,6 +1761,17 @@ void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionState { participant->mPower = 0.0; } + session->setMuteMic(muted); +} + +void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +{ + session->setSpeakerVolume(volume); +} + +void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +{ + session->setMicGain(volume); } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -1838,10 +1872,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) mSpeakerVolume = volume; mSpeakerVolumeDirty = true; } - if (mWebRTCDeviceInterface) - { - mWebRTCDeviceInterface->setSpeakerVolume(volume); - } + sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); } } @@ -1854,6 +1885,7 @@ void LLWebRTCVoiceClient::setMicGain(F32 volume) mMicVolume = scaled_volume; mMicVolumeDirty = true; } + sessionState::for_each(boost::bind(predSetMicGain, _1, scaled_volume)); } ///////////////////////////// @@ -2059,7 +2091,9 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::crea sessionState::ptr_t session(new sessionState()); session->mChannelID = channelID; - session->mWebRTCConnections[channelID] = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); + connectionPtr_t connection = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); + session->mWebRTCConnections[channelID] = connection; + session->mPrimaryConnectionID = channelID; // add agent as participant @@ -2190,6 +2224,9 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; result = sessionState::createSession(channel_id, parcel_local_id); + result->setMuteMic(mMuteMic); + result->setMicGain(mMicVolume); + result->setSpeakerVolume(mSpeakerVolume); if (LLVoiceClient::instance().getVoiceEffectEnabled()) { @@ -2442,7 +2479,10 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 par mChannelID(channelID), mRegionID(regionID), mParcelLocalID(parcelLocalID), - mShutDown(false) + mShutDown(false), + mMuted(true), + mSpeakerVolume(0.0), + mMicGain(0.0) { mWebRTCPeerConnection = llwebrtc::newPeerConnection(); mWebRTCPeerConnection->setSignalingObserver(this); @@ -2821,6 +2861,9 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_SESSION_ESTABLISHED: { + mWebRTCAudioInterface->setMute(mMuted); + mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); + mWebRTCAudioInterface->setSendVolume(mMicGain); LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID); setVoiceConnectionState(VOICE_STATE_SESSION_UP); } @@ -2958,3 +3001,30 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } + +void LLVoiceWebRTCConnection::setMuteMic(bool muted) +{ + mMuted = true; + 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); + } +} diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 456681ed25..f0549495e1 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -336,6 +336,10 @@ public: void OnConnectionFailure(const std::string &channelID); void sendData(const std::string &data); + + void setMuteMic(bool muted); + void setMicGain(F32 volume); + void setSpeakerVolume(F32 volume); static void for_each(sessionFunc_t func); @@ -350,6 +354,10 @@ public: std::string mName; std::string mErrorStatusString; std::queue mTextMsgQueue; + + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; LLUUID mIMSessionID; LLUUID mCallerID; @@ -404,6 +412,9 @@ public: static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_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); + ////////////////////////////// /// @name TVC/Server management and communication //@{ @@ -746,6 +757,9 @@ class LLVoiceWebRTCConnection : bool connectionStateMachine(); void sendData(const std::string &data); + void setMuteMic(bool muted); + void setMicGain(F32 volume); + void setSpeakerVolume(F32 volume); void shutDown() { @@ -811,6 +825,10 @@ protected: bool mShutDown; + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; + std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- cgit v1.2.3 From e5a95a96bface28bbd37afe655209d04f03cd250 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 1 Dec 2023 01:28:38 -0800 Subject: Setting volume for remote stream needs to happen in signaling thread --- indra/llwebrtc/llwebrtc.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 4741ed92a3..2bcbb135c0 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -620,26 +620,29 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute) void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume) { - auto receivers = mPeerConnection->GetReceivers(); - - for (auto &receiver : receivers) - { - for (auto& stream : receiver->streams()) + mWebRTCImpl->PostSignalingTask( + [this, volume]() { - for (auto& track : stream->GetAudioTracks()) + auto receivers = mPeerConnection->GetReceivers(); + + for (auto &receiver : receivers) { - track->GetSource()->SetVolume(volume); + for (auto &stream : receiver->streams()) + { + for (auto &track : stream->GetAudioTracks()) + { + track->GetSource()->SetVolume(volume); + } + } } - } - } + }); } void LLWebRTCPeerConnectionImpl::setSendVolume(float volume) { if (mLocalStream) { - auto track = mLocalStream->FindAudioTrack("SLStream"); - if (track) + for (auto &track : mLocalStream->GetAudioTracks()) { track->GetSource()->SetVolume(volume); } -- cgit v1.2.3 From 4f4f9032cb56712b2b514b5eea46ddda54b4094b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 4 Dec 2023 14:22:40 -0800 Subject: Mute using enable. Muting using the device module microphone mute was muting other applications, speakers, and so on. Instead, we mute by enabling/disabling the input and output streams. --- indra/llwebrtc/llwebrtc.cpp | 34 ++++++++++++++++++---------------- indra/newview/llvoicewebrtc.cpp | 16 ---------------- indra/newview/llvoicewebrtc.h | 1 - 3 files changed, 18 insertions(+), 33 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 2bcbb135c0..9fd8fd8ee6 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -287,16 +287,11 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) break; } } - mTuningDeviceModule->SetSpeakerMute(true); bool was_tuning_playing = mTuningDeviceModule->Playing(); if (was_tuning_playing) { mTuningDeviceModule->StopPlayout(); } - if (mPeerDeviceModule) - { - mPeerDeviceModule->SetSpeakerMute(true); - } mTuningDeviceModule->SetPlayoutDevice(tuningPlayoutDevice); mTuningDeviceModule->InitSpeaker(); @@ -326,10 +321,8 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) mPeerDeviceModule->InitSpeaker(); mPeerDeviceModule->InitPlayout(); mPeerDeviceModule->StartPlayout(); - mPeerDeviceModule->SetSpeakerMute(false); } - mTuningDeviceModule->SetSpeakerMute(false); }); } @@ -369,29 +362,25 @@ void LLWebRTCImpl::setTuningMode(bool enable) { mTuningDeviceModule->StartRecording(); - mTuningDeviceModule->SetMicrophoneMute(false); - - - mTuningDeviceModule->SetSpeakerMute(false); if (mPeerDeviceModule) { mPeerDeviceModule->StopRecording(); - mPeerDeviceModule->SetSpeakerMute(true); } } else { + mTuningDeviceModule->StartRecording(); if (mPeerDeviceModule) { mPeerDeviceModule->StartRecording(); - mPeerDeviceModule->SetSpeakerMute(false); } } }); for (auto& connection : mPeerConnections) { - connection->enableTracks(enable ? false : !mMute); + connection->enableSenderTracks(enable ? false : !mMute); + connection->enableReceiverTracks(!enable); } } @@ -409,7 +398,7 @@ LLWebRTCPeerConnection * LLWebRTCImpl::newPeerConnection() peerConnection->init(this); mPeerConnections.emplace_back(peerConnection); - peerConnection->enableTracks(!mMute); + peerConnection->enableSenderTracks(!mMute); return peerConnection.get(); } @@ -570,7 +559,7 @@ void LLWebRTCPeerConnectionImpl::shutdownConnection() }); } -void LLWebRTCPeerConnectionImpl::enableTracks(bool enable) +void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable) { // set_enabled shouldn't be done on the worker thread if (mPeerConnection) @@ -583,6 +572,19 @@ void LLWebRTCPeerConnectionImpl::enableTracks(bool 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); + } + } +} + void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp) { RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 435e2e1245..ddd757c39f 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -261,7 +261,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mEarLocation(0), mSpeakerVolumeDirty(true), - mSpeakerMuteDirty(true), mMicVolume(0), mMicVolumeDirty(true), @@ -583,16 +582,7 @@ LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices() void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) { - bool inTuningMode = mIsInTuningMode; - if (inTuningMode) - { - tuningStop(); - } mWebRTCDeviceInterface->setCaptureDevice(name); - if (inTuningMode) - { - tuningStart(); - } } void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { @@ -1863,12 +1853,6 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) if (volume != mSpeakerVolume) { { - int min_volume = 0.0; - if ((volume == min_volume) || (mSpeakerVolume == min_volume)) - { - mSpeakerMuteDirty = true; - } - mSpeakerVolume = volume; mSpeakerVolumeDirty = true; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f0549495e1..2eb74ed2cb 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -645,7 +645,6 @@ private: S32 mEarLocation; bool mSpeakerVolumeDirty; - bool mSpeakerMuteDirty; float mSpeakerVolume; int mMicVolume; -- cgit v1.2.3 From dd250273f476a96e60e7953915d76f6e2dd765ec Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 4 Dec 2023 14:26:12 -0800 Subject: missed file --- indra/llwebrtc/llwebrtc_impl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index efb0d3add3..cc22c30f35 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -273,7 +273,8 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, void OnMessage(const webrtc::DataBuffer& buffer) override; // Helpers - void enableTracks(bool enable); + void enableSenderTracks(bool enable); + void enableReceiverTracks(bool enable); protected: -- cgit v1.2.3 From b98d6020698c44b55658873aa21199f938cc1b8e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 4 Dec 2023 15:39:36 -0800 Subject: Voice was not renegotiating when re-enabled. --- indra/newview/llvoicewebrtc.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index ddd757c39f..a8285f9aab 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -357,6 +357,8 @@ void LLWebRTCVoiceClient::cleanUp() { LL_DEBUGS("Voice") << LL_ENDL; + mNextAudioSession.reset(); + mAudioSession.reset(); sessionState::deleteAllSessions(); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -470,16 +472,19 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { while (!sShuttingDown) { + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + if (!mVoiceEnabled) + { + continue; + } // add session for region or parcel voice. LLViewerRegion *regionp = gAgent.getRegion(); if (!regionp) { - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); continue; } if (regionp->getRegionID().isNull()) { - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); continue; } if (!mAudioSession || mAudioSession->mIsSpatial) @@ -503,7 +508,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() sessionState::for_each(boost::bind(predProcessSessionStates, _1)); sendPositionAndVolumeUpdate(true); updateOwnVolume(); - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); } } catch (const LLCoros::Stop&) @@ -1801,6 +1805,7 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); gAgent.setVoiceConnected(false); status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; + cleanUp(); } notifyStatusObservers(status); @@ -2261,6 +2266,7 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) void LLWebRTCVoiceClient::sessionState::deleteAllSessions() { mSessions.clear(); + } void LLWebRTCVoiceClient::verifySessionState(void) -- cgit v1.2.3 From 86b549cb793c3a368c266893d21c0ad8902863ef Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 5 Dec 2023 20:10:33 -0800 Subject: Fix crash when disconnecting. When disconnecting, we need to wait for any outstanding http calls to complete as the handlers may use the session objects. Also, reap empty sessions. --- indra/newview/llvoicewebrtc.cpp | 54 +++++++++++++++++++++++++++++++---------- indra/newview/llvoicewebrtc.h | 1 + 2 files changed, 42 insertions(+), 13 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a8285f9aab..9c229f1144 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -411,6 +411,7 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) { mAudioSession = mNextAudioSession; mNextAudioSession.reset(); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); } @@ -419,7 +420,6 @@ void LLWebRTCVoiceClient::sessionState::OnConnectionEstablished(const std::strin { if (channelID == mPrimaryConnectionID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } } @@ -506,6 +506,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } updatePosition(); sessionState::for_each(boost::bind(predProcessSessionStates, _1)); + sessionState::reapEmptySessions(); sendPositionAndVolumeUpdate(true); updateOwnVolume(); } @@ -1316,13 +1317,19 @@ bool LLWebRTCVoiceClient::switchChannel( // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. sessionTerminate(); - mAudioSession->shutdownAllConnections(); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + deleteSession(mAudioSession); } + if (channelID.empty()) { // Leave any channel we may be in LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + if (mNextAudioSession) + { + mAudioSession->shutdownAllConnections(); + } sessionStatePtr_t oldSession = mNextAudioSession; mNextAudioSession.reset(); @@ -1339,12 +1346,15 @@ bool LLWebRTCVoiceClient::switchChannel( } else { - LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; - - mNextAudioSession = addSession(channelID, parcel_local_id); - mNextAudioSession->mIsSpatial = spatial; - mNextAudioSession->mReconnect = !no_reconnect; - mNextAudioSession->mIsP2P = is_p2p; + if (mNextAudioSession) + { + deleteSession(mNextAudioSession); + } + LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; + mNextAudioSession = addSession(channelID, parcel_local_id); + mNextAudioSession->mIsSpatial = spatial; + mNextAudioSession->mReconnect = !no_reconnect; + mNextAudioSession->mIsP2P = is_p2p; } } @@ -2138,7 +2148,7 @@ void LLWebRTCVoiceClient::sessionState::reapEmptySessions() std::map::iterator iter; for (iter = mSessions.begin(); iter != mSessions.end();) { - if (!iter->second->isEmpty()) + if (iter->second->isEmpty()) { iter = mSessions.erase(iter); } @@ -2266,7 +2276,6 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) void LLWebRTCVoiceClient::sessionState::deleteAllSessions() { mSessions.clear(); - } void LLWebRTCVoiceClient::verifySessionState(void) @@ -2470,6 +2479,7 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 par mRegionID(regionID), mParcelLocalID(parcelLocalID), mShutDown(false), + mOutstandingRequests(0), mMuted(true), mSpeakerVolume(0.0), mMicGain(0.0) @@ -2526,6 +2536,7 @@ void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD return; } mTrickling = false; + mOutstandingRequests--; } void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) @@ -2546,6 +2557,7 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS body, boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1), boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); + mOutstandingRequests++; } else { @@ -2553,6 +2565,7 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mTrickling = false; } + mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) @@ -2721,6 +2734,7 @@ void LLVoiceWebRTCConnection::processIceUpdates() body, boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); + mOutstandingRequests++; mTrickling = true; } } @@ -2765,6 +2779,7 @@ bool LLVoiceWebRTCConnection::requestVoiceConnection() body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + mOutstandingRequests++; return true; } @@ -2780,6 +2795,12 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result { mRemoteChannelSDP = result["jsep"]["sdp"].asString(); } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + mOutstandingRequests--; + return; + } std::string voiceAccountServerUri; std::string voiceUserName = gAgent.getID().asString(); std::string voicePassword = ""; // no password for now. @@ -2789,6 +2810,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << mRemoteChannelSDP << LL_ENDL; mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) @@ -2805,12 +2827,14 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + mOutstandingRequests++; } else { LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } + mOutstandingRequests--; } bool LLVoiceWebRTCConnection::connectionStateMachine() @@ -2897,7 +2921,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } else { - return false; + return mOutstandingRequests > 0; } } break; @@ -2962,6 +2986,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + mOutstandingRequests++; return true; } @@ -2972,6 +2997,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res return; } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) @@ -2986,10 +3012,12 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); + mOutstandingRequests++; } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::setMuteMic(bool muted) diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 2eb74ed2cb..af6e67c8b4 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -823,6 +823,7 @@ protected: S32 mParcelLocalID; bool mShutDown; + S32 mOutstandingRequests; bool mMuted; F32 mMicGain; -- cgit v1.2.3 From b58342a4e08c3adecb9da6d53aae791cc29a917b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 7 Dec 2023 19:47:37 -0800 Subject: Rework VU meter level processing to be closer to Vivox --- indra/llwebrtc/llwebrtc.cpp | 6 +- indra/newview/llvoicewebrtc.cpp | 134 ++++++++++++++++++---------------------- indra/newview/llvoicewebrtc.h | 9 +-- 3 files changed, 67 insertions(+), 82 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 9fd8fd8ee6..32a75d3675 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -54,7 +54,7 @@ void LLAudioDeviceObserver::OnCaptureData(const void *audio_samples, const short *samples = (const short *) audio_samples; for (size_t index = 0; index < num_samples * num_channels; index++) { - float sample = (static_cast(samples[index]) / (float) 32768); + float sample = (static_cast(samples[index]) / (float) 32767); energy += sample * sample; } @@ -384,9 +384,9 @@ void LLWebRTCImpl::setTuningMode(bool enable) } } -float LLWebRTCImpl::getTuningAudioLevel() { return 20 * mTuningAudioDeviceObserver->getMicrophoneEnergy(); } +float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mTuningAudioDeviceObserver->getMicrophoneEnergy()); } -float LLWebRTCImpl::getPeerAudioLevel() { return 20 * mPeerAudioDeviceObserver->getMicrophoneEnergy(); } +float LLWebRTCImpl::getPeerAudioLevel() { return -20 * log10f(mPeerAudioDeviceObserver->getMicrophoneEnergy()); } // // Helpers diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9c229f1144..b9755cdda1 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -86,9 +86,10 @@ extern void handle_voice_morphing_subscribe(); namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; + const F32 LEVEL_SCALE_WEBRTC = 0.01f; const F32 SPEAKING_TIMEOUT = 1.f; - const F32 SPEAKING_AUDIO_LEVEL = 0.05; + const F32 SPEAKING_AUDIO_LEVEL = 0.10; static const std::string VOICE_SERVER_TYPE = "WebRTC"; @@ -234,9 +235,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mSpatialJoiningNum(0), mTuningMode(false), - mTuningEnergy(0.0f), - mTuningMicVolume(0), - mTuningMicVolumeDirty(true), + mTuningMicGain(0.0), mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume mTuningSpeakerVolumeDirty(true), mDevicesListUpdated(false), @@ -261,8 +260,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mEarLocation(0), mSpeakerVolumeDirty(true), - mMicVolume(0), - mMicVolumeDirty(true), + mMicGain(0.0), mVoiceEnabled(false), @@ -314,25 +312,25 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : LLWebRTCVoiceClient::~LLWebRTCVoiceClient() { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } sShuttingDown = true; } //--------------------------------------------------- -void LLWebRTCVoiceClient::init(LLPumpIO *pump) +void LLWebRTCVoiceClient::init(LLPumpIO* pump) { - // constructor will set up LLVoiceClient::getInstance() - sPump = pump; + // constructor will set up LLVoiceClient::getInstance() + sPump = pump; -// LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", -// boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); + // LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", + // boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); llwebrtc::init(); - mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); + mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); mWebRTCDeviceInterface->setDevicesObserver(this); } @@ -342,9 +340,9 @@ void LLWebRTCVoiceClient::terminate() { return; } - - mRelogRequested = false; - mVoiceEnabled = false; + + mRelogRequested = false; + mVoiceEnabled = false; llwebrtc::terminate(); sShuttingDown = true; @@ -356,10 +354,10 @@ void LLWebRTCVoiceClient::terminate() void LLWebRTCVoiceClient::cleanUp() { LL_DEBUGS("Voice") << LL_ENDL; - + mNextAudioSession.reset(); mAudioSession.reset(); - sessionState::deleteAllSessions(); + sessionState::deleteAllSessions(); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -367,7 +365,7 @@ void LLWebRTCVoiceClient::cleanUp() const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() { - return mVoiceVersion; + return mVoiceVersion; } //--------------------------------------------------- @@ -375,15 +373,15 @@ const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() void LLWebRTCVoiceClient::updateSettings() { setVoiceEnabled(voiceEnabled()); - setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + 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); - setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); + 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); + setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); } @@ -395,7 +393,7 @@ void LLWebRTCVoiceClient::predOnConnectionEstablished(const LLWebRTCVoiceClient: session->OnConnectionEstablished(channelID); } -void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID) +void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) { session->OnConnectionFailure(channelID); } @@ -405,7 +403,7 @@ void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); } -void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID) { if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) { @@ -413,6 +411,10 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) mNextAudioSession.reset(); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); } @@ -658,13 +660,7 @@ bool LLWebRTCVoiceClient::inTuningMode() void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) { - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mTuningMicVolume) - { - mTuningMicVolume = scaled_volume; - mTuningMicVolumeDirty = true; - } + mTuningMicGain = volume; } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) @@ -679,7 +675,7 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return mWebRTCDeviceInterface->getTuningAudioLevel(); + return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain/2.1; } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -739,10 +735,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) F32 audio_level = 0.0; uint32_t uint_audio_level = 0.0; - if (!mMuteMic) + if (!mMuteMic && !mTuningMode) { - audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel() * mMicVolume; - uint_audio_level = (uint32_t) (audio_level * 128); + audio_level = (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + uint_audio_level = (uint32_t) (audio_level*128); } @@ -810,9 +806,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) void LLWebRTCVoiceClient::updateOwnVolume() { F32 audio_level = 0.0; - if (!mMuteMic) + if (!mMuteMic && !mTuningMode) { - audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); + audio_level = (F32) (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + LL_DEBUGS("Voice") << "Level " << audio_level << LL_ENDL; } sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); @@ -823,7 +820,7 @@ void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::session participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); if (participant) { - participant->mPower = audio_level; + participant->mLevel = audio_level; participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; } } @@ -991,7 +988,7 @@ LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) mIsSpeaking(false), mIsModeratorMuted(false), mLastSpokeTimestamp(0.f), - mPower(0.f), + mLevel(0.f), mVolume(LLVoiceClient::VOLUME_DEFAULT), mUserVolume(0), mOnMuteList(false), @@ -1763,7 +1760,7 @@ void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionState participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); if (participant) { - participant->mPower = 0.0; + participant->mLevel = 0.0; } session->setMuteMic(muted); } @@ -1773,9 +1770,9 @@ void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessio session->setSpeakerVolume(volume); } -void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 gain) { - session->setMicGain(volume); + session->setMicGain(gain); } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -1875,16 +1872,13 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) } } -void LLWebRTCVoiceClient::setMicGain(F32 volume) -{ - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mMicVolume) - { - mMicVolume = scaled_volume; - mMicVolumeDirty = true; - } - sessionState::for_each(boost::bind(predSetMicGain, _1, scaled_volume)); +void LLWebRTCVoiceClient::setMicGain(F32 gain) +{ + if (gain != mMicGain) + { + mMicGain = gain; + sessionState::for_each(boost::bind(predSetMicGain, _1, gain)); + } } ///////////////////////////// @@ -1967,7 +1961,7 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if (participant) { - result = participant->mPower * 2.0; + result = participant->mLevel; } return result; } @@ -2224,7 +2218,7 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; result = sessionState::createSession(channel_id, parcel_local_id); result->setMuteMic(mMuteMic); - result->setMicGain(mMicVolume); + result->setMicGain(mMicGain); result->setSpeakerVolume(mSpeakerVolume); if (LLVoiceClient::instance().getVoiceEffectEnabled()) @@ -2639,12 +2633,12 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } else { - F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 128; + F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; // convert to decibles - participant->mPower = energyRMS; + participant->mLevel = level; /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we can use it at some point when we actually process frames. */ - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; + participant->mIsSpeaking = participant->mLevel > SPEAKING_AUDIO_LEVEL; } } } @@ -2898,14 +2892,8 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() break; case VOICE_STATE_DISCONNECT: - if (breakVoiceConnection(true)) - { - setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); - } - else - { - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); - } + breakVoiceConnection(true); + retry = 0; // Connected without issues break; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index af6e67c8b4..46faf52da5 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -280,7 +280,7 @@ public: std::string mDisplayName; LLFrameTimer mSpeakingTimeout; F32 mLastSpokeTimestamp; - F32 mPower; + F32 mLevel; F32 mVolume; std::string mGroupID; int mUserVolume; @@ -559,9 +559,7 @@ private: static void idle(void *user_data); bool mTuningMode; - float mTuningEnergy; - int mTuningMicVolume; - bool mTuningMicVolumeDirty; + F32 mTuningMicGain; int mTuningSpeakerVolume; bool mTuningSpeakerVolumeDirty; bool mDevicesListUpdated; // set to true when the device list has been updated @@ -647,8 +645,7 @@ private: bool mSpeakerVolumeDirty; float mSpeakerVolume; - int mMicVolume; - bool mMicVolumeDirty; + F32 mMicGain; bool mVoiceEnabled; -- cgit v1.2.3 From 4d37d1c32f7998b45786dd2de596019b6ba3269d Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 7 Dec 2023 20:35:05 -0800 Subject: fix mac build break --- indra/newview/llvoicewebrtc.cpp | 7 ------- 1 file changed, 7 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b9755cdda1..9f290b9d4a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -123,13 +123,6 @@ namespace { const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; } -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 WebRTC levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 - return 30 + (int)(volume * 20.0f); -} - /////////////////////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 2a375d25dc2bb6e063d8e596ce3b4bd2c61cd96b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 8 Dec 2023 12:09:02 -0800 Subject: fix device selection (hopefully) --- indra/llwebrtc/llwebrtc.cpp | 17 +++++++++++++++-- indra/llwebrtc/llwebrtc_impl.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 32a75d3675..12997e34d3 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -300,10 +300,10 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) { mTuningDeviceModule->StartPlayout(); } - renderDeviceCount = mPeerDeviceModule->PlayoutDevices(); if (mPeerDeviceModule) { + renderDeviceCount = mPeerDeviceModule->PlayoutDevices(); for (int16_t i = 0; i < renderDeviceCount; i++) { char name[webrtc::kAdmMaxDeviceNameSize]; @@ -317,6 +317,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) break; } } + mPeerDeviceModule->StopPlayout(); mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); mPeerDeviceModule->InitSpeaker(); mPeerDeviceModule->InitPlayout(); @@ -379,7 +380,14 @@ void LLWebRTCImpl::setTuningMode(bool enable) }); for (auto& connection : mPeerConnections) { - connection->enableSenderTracks(enable ? false : !mMute); + if (enable) + { + connection->enableSenderTracks(false); + } + else + { + connection->resetMute(); + } connection->enableReceiverTracks(!enable); } } @@ -620,6 +628,11 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute) } } +void LLWebRTCPeerConnectionImpl::resetMute() +{ + setMute(mMute); +} + void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume) { mWebRTCImpl->PostSignalingTask( diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index cc22c30f35..6a84f67ef5 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -273,6 +273,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, void OnMessage(const webrtc::DataBuffer& buffer) override; // Helpers + void resetMute(); void enableSenderTracks(bool enable); void enableReceiverTracks(bool enable); -- cgit v1.2.3 From 9f6d8effb4c5730ff2bbb8f354e341b95824437d Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 12 Dec 2023 20:46:34 -0800 Subject: Better renegotiation support for parcel voice Better handle starting up and shutting down WebRTC connections simultaneously. --- indra/llwebrtc/llwebrtc.cpp | 84 ++++++++++-------- indra/llwebrtc/llwebrtc.h | 1 + indra/newview/llvoicewebrtc.cpp | 183 +++++++++++++++++++++++++++------------- indra/newview/llvoicewebrtc.h | 8 +- 4 files changed, 180 insertions(+), 96 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 12997e34d3..e55d94d128 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -83,9 +83,10 @@ void LLAudioDeviceObserver::OnRenderData(const void *audio_samples, void LLWebRTCImpl::init() { RTC_DCHECK(mPeerConnectionFactory); - mPlayoutDevice = -1; - mRecordingDevice = -1; + mPlayoutDevice = 0; + mRecordingDevice = 0; rtc::InitializeSSL(); + rtc::LogMessage::LogToDebug(rtc::LS_WARNING); mTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory(); mNetworkThread = rtc::Thread::CreateWithSocketServer(); @@ -145,10 +146,9 @@ void LLWebRTCImpl::init() mPeerDeviceModule->InitSpeaker(); mPeerDeviceModule->InitRecording(); mPeerDeviceModule->InitPlayout(); - mPeerDeviceModule->StartPlayout(); - mPeerDeviceModule->StartRecording(); }); - + + apm->ApplyConfig(apm_config); mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(), mWorkerThread.get(), mSignalingThread.get(), @@ -159,7 +159,12 @@ void LLWebRTCImpl::init() nullptr /* video_decoder_factory */, nullptr /* audio_mixer */, apm); - apm->ApplyConfig(apm_config); + mWorkerThread->BlockingCall( + [this]() + { + mPeerDeviceModule->StartPlayout(); + mPeerDeviceModule->StartRecording(); + }); } void LLWebRTCImpl::terminate() @@ -235,15 +240,14 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id) mTuningDeviceModule->InitMicrophone(); mTuningDeviceModule->InitRecording(); mTuningDeviceModule->StartRecording(); - bool was_peer_recording = false; if (mPeerDeviceModule) { - int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); + int16_t captureDeviceCount = mPeerDeviceModule->RecordingDevices(); for (int16_t i = 0; i < captureDeviceCount; i++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; - mTuningDeviceModule->RecordingDeviceName(i, name, guid); + mPeerDeviceModule->RecordingDeviceName(i, name, guid); if (id == guid || id == "Default") // first one in list is default { RTC_LOG(LS_INFO) @@ -252,11 +256,13 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id) break; } } - was_peer_recording = mPeerDeviceModule->Recording(); + bool was_peer_recording = mPeerDeviceModule->Recording(); if (was_peer_recording) { mPeerDeviceModule->StopRecording(); } + + mPeerDeviceModule->StopRecording(); mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); mPeerDeviceModule->InitMicrophone(); mPeerDeviceModule->InitRecording(); @@ -432,7 +438,15 @@ void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl) } void LLWebRTCPeerConnectionImpl::terminate() { - shutdownConnection(); + mWebRTCImpl->SignalingBlockingCall( + [this]() + { + if (mPeerConnection) + { + mPeerConnection->Close(); + mPeerConnection = nullptr; + } + }); } void LLWebRTCPeerConnectionImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); } @@ -477,7 +491,6 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() else { RTC_LOG(LS_ERROR) << __FUNCTION__ << "Error creating peer connection: " << error_or_peer_connection.error().message(); - shutdownConnection(); return false; } @@ -546,25 +559,19 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() void LLWebRTCPeerConnectionImpl::shutdownConnection() { - mWebRTCImpl->SignalingBlockingCall( - [this]() - { - if (mPeerConnection) - { - mPeerConnection->Close(); - mPeerConnection = nullptr; - } - }); - - mWebRTCImpl->NetworkBlockingCall( - [this]() - { - if (mDataChannel) - { - mDataChannel->Close(); - mDataChannel = nullptr; - } - }); + mWebRTCImpl->PostSignalingTask( + [this]() + { + if (mPeerConnection) + { + mPeerConnection->Close(); + mPeerConnection = nullptr; + } + for (auto &observer : mSignalingObserverList) + { + observer->OnPeerShutDown(); + } + }); } void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable) @@ -638,15 +645,18 @@ void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume) mWebRTCImpl->PostSignalingTask( [this, volume]() { - auto receivers = mPeerConnection->GetReceivers(); - - for (auto &receiver : receivers) + if (mPeerConnection) { - for (auto &stream : receiver->streams()) + auto receivers = mPeerConnection->GetReceivers(); + + for (auto &receiver : receivers) { - for (auto &track : stream->GetAudioTracks()) + for (auto &stream : receiver->streams()) { - track->GetSource()->SetVolume(volume); + for (auto &track : stream->GetAudioTracks()) + { + track->GetSource()->SetVolume(volume); + } } } } diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 9766f2f231..f84e998245 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -125,6 +125,7 @@ class LLWebRTCSignalingObserver virtual void OnRenegotiationNeeded() = 0; virtual void OnAudioEstablished(LLWebRTCAudioInterface *audio_interface) = 0; virtual void OnDataChannelReady(LLWebRTCDataInterface *data_interface) = 0; + virtual void OnPeerShutDown() = 0; }; class LLWebRTCPeerConnection diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9f290b9d4a..ec1e6a353d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -86,10 +86,10 @@ extern void handle_voice_morphing_subscribe(); namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; - const F32 LEVEL_SCALE_WEBRTC = 0.01f; + const F32 LEVEL_SCALE_WEBRTC = 0.008f; const F32 SPEAKING_TIMEOUT = 1.f; - const F32 SPEAKING_AUDIO_LEVEL = 0.10; + const F32 SPEAKING_AUDIO_LEVEL = 0.40; static const std::string VOICE_SERVER_TYPE = "WebRTC"; @@ -121,9 +121,19 @@ namespace { // Maximum length of capture buffer recordings in seconds. const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; +} // namespace +float LLWebRTCVoiceClient::getAudioLevel() +{ + if (mIsInTuningMode) + { + return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1; + } + else + { + return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + } } - /////////////////////////////////////////////////////////////////////////////////////////////// void LLVoiceWebRTCStats::reset() @@ -350,7 +360,7 @@ void LLWebRTCVoiceClient::cleanUp() mNextAudioSession.reset(); mAudioSession.reset(); - sessionState::deleteAllSessions(); + sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -393,6 +403,14 @@ void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::ses void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) { + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); } @@ -468,10 +486,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() while (!sShuttingDown) { llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - if (!mVoiceEnabled) - { - continue; - } // add session for region or parcel voice. LLViewerRegion *regionp = gAgent.getRegion(); if (!regionp) @@ -482,7 +496,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } - if (!mAudioSession || mAudioSession->mIsSpatial) + if (mVoiceEnabled && (!mAudioSession || mAudioSession->mIsSpatial)) { // check to see if parcel changed. std::string channelID = regionp->getRegionID().asString(); @@ -499,11 +513,14 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() setSpatialChannel(channelID, "", parcel_local_id); } } - updatePosition(); sessionState::for_each(boost::bind(predProcessSessionStates, _1)); - sessionState::reapEmptySessions(); - sendPositionAndVolumeUpdate(true); - updateOwnVolume(); + reapEmptySessions(); + if (mVoiceEnabled) + { + updatePosition(); + sendPositionAndVolumeUpdate(true); + updateOwnVolume(); + } } } catch (const LLCoros::Stop&) @@ -526,15 +543,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() cleanUp(); } -bool LLWebRTCVoiceClient::performMicTuning() -{ - LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; - - mIsInTuningMode = false; - - //--------------------------------------------------------------------- - return true; -} //========================================================================= @@ -665,10 +673,11 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) mTuningSpeakerVolumeDirty = true; } } - + + float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain/2.1; + return getAudioLevel(); } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -730,7 +739,7 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) if (!mMuteMic && !mTuningMode) { - audio_level = (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + audio_level = getAudioLevel(); uint_audio_level = (uint32_t) (audio_level*128); } @@ -801,8 +810,8 @@ void LLWebRTCVoiceClient::updateOwnVolume() { F32 audio_level = 0.0; if (!mMuteMic && !mTuningMode) { - audio_level = (F32) (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; - LL_DEBUGS("Voice") << "Level " << audio_level << LL_ENDL; + audio_level = getAudioLevel(); + LL_WARNS("Voice") << "Level " << audio_level << LL_ENDL; } sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); @@ -2052,6 +2061,22 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() return mAreaVoiceDisabled; } +void LLWebRTCVoiceClient::reapEmptySessions() +{ + sessionState::reapEmptySessions(); + + // mAudioSession or mNextAudioSession was reaped, + // so reset them. + if (mAudioSession && !sessionState::hasSession(mAudioSession->mChannelID)) + { + mAudioSession.reset(); + } + if (mNextAudioSession && !sessionState::hasSession(mNextAudioSession->mChannelID)) + { + mNextAudioSession.reset(); + } +} + //------------------------------------------------------------------------ std::map LLWebRTCVoiceClient::sessionState::mSessions; @@ -2241,6 +2266,11 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std return result; } +void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) +{ + session->shutdownAllConnections(); +} + void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) { // At this point, the session should be unhooked from all lists and all state should be consistent. @@ -2260,11 +2290,6 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::sessionState::deleteAllSessions() -{ - mSessions.clear(); -} - void LLWebRTCVoiceClient::verifySessionState(void) { sessionState::VerifySessions(); @@ -2356,6 +2381,8 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt << ", proximal is " << inSpatialChannel() << LL_ENDL; + mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN; + for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end(); ) @@ -2365,10 +2392,7 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt // In case onError() deleted an entry. it = mStatusObservers.upper_bound(observer); } - mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN; - { - } // skipped to avoid speak button blinking if ( status != LLVoiceClientStatusObserver::STATUS_JOINING && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL @@ -2483,6 +2507,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() // by llwebrtc::terminate() on shutdown. return; } + assert(mOutstandingRequests == 0); mWebRTCPeerConnection->unsetSignalingObserver(this); llwebrtc::freePeerConnection(mWebRTCPeerConnection); mWebRTCPeerConnection = nullptr; @@ -2544,14 +2569,16 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS body, boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1), boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); - mOutstandingRequests++; + return; } - else + + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; + if (!mShutDown) { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - mTrickling = false; } + mTrickling = false; + mOutstandingRequests--; } @@ -2655,7 +2682,16 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + if (!mShutDown) + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } +} + +void LLVoiceWebRTCConnection::OnPeerShutDown() +{ + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::processIceUpdates() @@ -2664,6 +2700,10 @@ void LLVoiceWebRTCConnection::processIceUpdates() { return; } + if (mShutDown) + { + return; + } LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { @@ -2814,26 +2854,25 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); - mOutstandingRequests++; - } - else - { - LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } + LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; } bool LLVoiceWebRTCConnection::connectionStateMachine() -{ - U32 retry = 0; - +{ processIceUpdates(); switch (getVoiceConnectionState()) { case VOICE_STATE_START_SESSION: { + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + } mTrickling = false; mIceCompleted = false; setVoiceConnectionState(VOICE_STATE_WAIT_FOR_SESSION_START); @@ -2845,9 +2884,18 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } 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; + } if (!requestVoiceConnection()) { setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); @@ -2858,10 +2906,19 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } 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; + } mWebRTCAudioInterface->setMute(mMuted); mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); mWebRTCAudioInterface->setSendVolume(mMicGain); @@ -2886,8 +2943,6 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_DISCONNECT: breakVoiceConnection(true); - - retry = 0; // Connected without issues break; case VOICE_STATE_WAIT_FOR_EXIT: @@ -2934,10 +2989,6 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) mWebRTCDataInterface = nullptr; } mWebRTCAudioInterface = nullptr; - if (mWebRTCPeerConnection) - { - mWebRTCPeerConnection->shutdownConnection(); - } LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { @@ -2967,6 +3018,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); mOutstandingRequests++; return true; } @@ -2977,7 +3029,16 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res { return; } - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + + if (mWebRTCPeerConnection) + { + mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + } mOutstandingRequests--; } @@ -2995,9 +3056,17 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); + return; + } + if (mWebRTCPeerConnection) + { mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); mOutstandingRequests--; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 46faf52da5..f311f241dc 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -381,7 +381,7 @@ public: LLUUID mVoiceFontID; static void VerifySessions(); - static void deleteAllSessions(); + static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } private: @@ -414,6 +414,8 @@ public: 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); + void reapEmptySessions(); ////////////////////////////// /// @name TVC/Server management and communication @@ -535,6 +537,8 @@ public: void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); private: + + float getAudioLevel(); LLVoiceVersionInfo mVoiceVersion; @@ -544,7 +548,6 @@ private: void voiceConnectionStateMachine(); - bool performMicTuning(); //--- /// Clean up objects created during a voice session. void cleanUp(); @@ -732,6 +735,7 @@ class LLVoiceWebRTCConnection : void OnOfferAvailable(const std::string &sdp) override; void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + void OnPeerShutDown() override; //@} ///////////////////////// -- cgit v1.2.3 From e9d30fda25c6104a77f51fb9db86f7933f56b22b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 18 Dec 2023 11:45:15 -0800 Subject: Touch up parcel voice enable/disable. --- indra/llwebrtc/llwebrtc.cpp | 203 +++++++++---------- indra/newview/llvoicewebrtc.cpp | 437 ++++++++++++---------------------------- indra/newview/llvoicewebrtc.h | 43 ++-- 3 files changed, 261 insertions(+), 422 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index e55d94d128..9b3ec2889b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -86,7 +86,9 @@ void LLWebRTCImpl::init() mPlayoutDevice = 0; mRecordingDevice = 0; rtc::InitializeSSL(); - rtc::LogMessage::LogToDebug(rtc::LS_WARNING); + rtc::LogMessage::LogToDebug(rtc::LS_NONE); + rtc::LogMessage::SetLogToStderr(true); + mTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory(); mNetworkThread = rtc::Thread::CreateWithSocketServer(); @@ -98,7 +100,7 @@ void LLWebRTCImpl::init() mSignalingThread = rtc::Thread::Create(); mSignalingThread->SetName("WebRTCSignalingThread", nullptr); mSignalingThread->Start(); - + mWorkerThread->PostTask( [this]() { @@ -466,93 +468,98 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() RTC_DCHECK(!mPeerConnection); mAnswerReceived = false; - 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); - - 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 false; - } - - 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); - } - - RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state(); - - cricket::AudioOptions audioOptions; - audioOptions.auto_gain_control = true; - audioOptions.echo_cancellation = false; // incompatible with opus stereo - audioOptions.noise_suppression = true; - - mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); - rtc::scoped_refptr 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); + 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 = false; // incompatible with opus stereo + audioOptions.noise_suppression = true; + + mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); + rtc::scoped_refptr 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; } @@ -839,7 +846,7 @@ void LLWebRTCPeerConnectionImpl::OnIceCandidate(const webrtc::IceCandidateInterf else { mCachedIceCandidates.push_back( - webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); + webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); } } @@ -878,21 +885,16 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface * sdp_mangled_stream << sdp_line << "\n"; } } - - webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str()); - - - - mPeerConnection->SetLocalDescription(std::unique_ptr( - webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str())), - rtc::scoped_refptr(this)); + 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::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(); } @@ -930,7 +932,6 @@ void LLWebRTCPeerConnectionImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError // void LLWebRTCPeerConnectionImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error) { - } // diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index ec1e6a353d..9a1b3525d7 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -455,17 +455,29 @@ void LLWebRTCVoiceClient::idle(void* user_data) // // -void LLWebRTCVoiceClient::predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t& session) +void LLWebRTCVoiceClient::sessionState::processSessionStates() { - session->processSessionStates(); + auto iter = mSessions.begin(); + while (iter != mSessions.end()) + { + if (!iter->second->processConnectionStates()) + { + iter = mSessions.erase(iter); + } + else + { + iter++; + } + } } -void LLWebRTCVoiceClient::sessionState::processSessionStates() +bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { - std::map::iterator iter; - for (iter = mWebRTCConnections.begin(); iter != mWebRTCConnections.end();) + std::list::iterator iter = mWebRTCConnections.begin(); + + while (iter != mWebRTCConnections.end()) { - if (!iter->second->connectionStateMachine()) + if (!iter->get()->connectionStateMachine()) { iter = mWebRTCConnections.erase(iter); } @@ -474,6 +486,7 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() ++iter; } } + return !mWebRTCConnections.empty(); } void LLWebRTCVoiceClient::voiceConnectionCoro() @@ -488,33 +501,39 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); // add session for region or parcel voice. LLViewerRegion *regionp = gAgent.getRegion(); - if (!regionp) + if (!regionp || regionp->getRegionID().isNull()) { continue; } - if (regionp->getRegionID().isNull()) - { - continue; - } - if (mVoiceEnabled && (!mAudioSession || mAudioSession->mIsSpatial)) + + if (mVoiceEnabled && (!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) { // check to see if parcel changed. std::string channelID = regionp->getRegionID().asString(); LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); S32 parcel_local_id = INVALID_PARCEL_ID; - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID && !parcel->getParcelFlagUseEstateVoiceChannel()) + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - parcel_local_id = parcel->getLocalID(); - channelID += "-" + std::to_string(parcel->getLocalID()); + if (!parcel->getParcelFlagAllowVoice()) + { + channelID.clear(); + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + parcel_local_id = parcel->getLocalID(); + channelID += "-" + std::to_string(parcel->getLocalID()); + } } - if (!mAudioSession || channelID != mAudioSession->mChannelID) + + if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || + (!mAudioSession && !channelID.empty()) || + (mAudioSession && channelID != mAudioSession->mChannelID)) { setSpatialChannel(channelID, "", parcel_local_id); } } - sessionState::for_each(boost::bind(predProcessSessionStates, _1)); - reapEmptySessions(); + sessionState::processSessionStates(); if (mVoiceEnabled) { updatePosition(); @@ -829,7 +848,7 @@ void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::session void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data, const std::string& volume_data) { - if (session->mIsSpatial && !spatial_data.empty()) + if (session->isSpatial() && !spatial_data.empty()) { session->sendData(spatial_data); } @@ -843,7 +862,7 @@ void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) { for (auto& connection : mWebRTCConnections) { - connection.second->sendData(data); + connection->sendData(data); } } @@ -852,7 +871,7 @@ void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) mMuted = muted; for (auto& connection : mWebRTCConnections) { - connection.second->setMuteMic(muted); + connection->setMuteMic(muted); } } @@ -861,7 +880,7 @@ void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) mMicGain = gain; for (auto& connection : mWebRTCConnections) { - connection.second->setMicGain(gain); + connection->setMicGain(gain); } } @@ -870,7 +889,7 @@ void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) mSpeakerVolume = volume; for (auto& connection : mWebRTCConnections) { - connection.second->setSpeakerVolume(volume); + connection->setSpeakerVolume(volume); } } @@ -878,52 +897,6 @@ void LLWebRTCVoiceClient::sendLocalAudioUpdates() { } - -void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) -{ - LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; - if(mAudioSession != session) - { - sessionStatePtr_t oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(mIsJoiningSession) - { - LLSD WebRTCevent(LLSDMap("channel", session->mChannelID) - ("session", "joined")); - - mWebRTCPump.post(WebRTCevent); - - if(!session->mIsChannel) - { - // this is a p2p session. Make sure the other end is added as a participant. - participantStatePtr_t participant(session->addParticipant(LLUUID(session->mChannelID))); - if(participant) - { - if(participant->mAvatarIDValid) - { - lookupName(participant->mAvatarID); - } - else if(!session->mName.empty()) - { - 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->mAvatarID << ")"<< LL_ENDL; - } - } - } -} - void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) @@ -950,19 +923,6 @@ void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) } -void LLWebRTCVoiceClient::leftAudioSession(const sessionStatePtr_t &session) -{ - if (mAudioSession == session) - { - LLSD WebRTCevent(LLSDMap("channel", session->mChannelID) - ("session", "removed")); - - mWebRTCPump.post(WebRTCevent); - } -} - - - void LLWebRTCVoiceClient::muteListChanged() { // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. @@ -1215,149 +1175,47 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co } } - -// Check for parcel boundary crossing -bool LLWebRTCVoiceClient::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. - // 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()) - { - if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) - { - // We have changed parcels. Initiate a parcel channel lookup. - if (update) - { - mCurrentParcelLocalID = parcelLocalID; - mCurrentRegionName = regionName; - } - return true; - } - } - } - return false; -} - -bool LLWebRTCVoiceClient::switchChannel( - const std::string channelID, - bool spatial, - bool no_reconnect, - bool is_p2p, - std::string hash, - S32 parcel_local_id) +bool LLWebRTCVoiceClient::switchChannel(const std::string channelID, + sessionState::ESessionType session_type, + S32 parcel_local_id) { - bool needsSwitch = false; - if (mAudioSession) { - if (mSessionTerminateRequested) + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + deleteSession(mAudioSession); + } + + if (channelID.empty()) + { + // Leave any channel we may be in + LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + + if (mNextAudioSession) { - // If a terminate has been requested, we need to compare against where the URI we're already headed to. - if(mNextAudioSession) - { - if (mNextAudioSession->mChannelID != channelID) - needsSwitch = true; - } - else - { - // mNextAudioSession is null -- this probably means we're on our way back to spatial. - if (!channelID.empty()) - { - // We do want to process a switch in this case. - needsSwitch = true; - } - } + deleteSession(mNextAudioSession); } - else + // If voice was on, turn it off + if (LLVoiceClient::getInstance()->getUserPTTState()) { - // Otherwise, compare against the URI we're in now. - if(mAudioSession) - { - if (mAudioSession->mChannelID != channelID) - { - needsSwitch = true; - } - } - else - { - if (!channelID.empty()) - { - // mAudioSession is null -- it's not clear what case would cause this. - // For now, log it as a warning and see if it ever crops up. - LL_WARNS("Voice") << "No current audio session... Forcing switch" << LL_ENDL; - needsSwitch = true; - } - } + LLVoiceClient::getInstance()->setUserPTTState(false); } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); } else { - if (!mNextAudioSession || mNextAudioSession->mChannelID != channelID) + if (mNextAudioSession) { - needsSwitch = true; + deleteSession(mNextAudioSession); } + LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; + mNextAudioSession = addSession(channelID, parcel_local_id); + mNextAudioSession->mSessionType = session_type; } - - if(needsSwitch) - { - if (mAudioSession) - { - // If we're already in a channel, or if we're joining one, terminate - // so we can rejoin with the new session data. - sessionTerminate(); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - deleteSession(mAudioSession); - } - - if (channelID.empty()) - { - // Leave any channel we may be in - LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; - - if (mNextAudioSession) - { - mAudioSession->shutdownAllConnections(); - } - sessionStatePtr_t oldSession = mNextAudioSession; - mNextAudioSession.reset(); - - // The old session may now need to be deleted. - reapSession(oldSession); - - // If voice was on, turn it off - if (LLVoiceClient::getInstance()->getUserPTTState()) - { - LLVoiceClient::getInstance()->setUserPTTState(false); - } - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - } - else - { - if (mNextAudioSession) - { - deleteSession(mNextAudioSession); - } - LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; - mNextAudioSession = addSession(channelID, parcel_local_id); - mNextAudioSession->mIsSpatial = spatial; - mNextAudioSession->mReconnect = !no_reconnect; - mNextAudioSession->mIsP2P = is_p2p; - } - } - - return needsSwitch; + return true; } void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) @@ -1376,7 +1234,7 @@ void LLWebRTCVoiceClient::setNonSpatialChannel( const std::string &uri, const std::string &credentials) { - switchChannel(uri, false, false, false, credentials); + switchChannel(uri, sessionState::SESSION_TYPE_P2P); } bool LLWebRTCVoiceClient::setSpatialChannel( @@ -1386,7 +1244,7 @@ bool LLWebRTCVoiceClient::setSpatialChannel( LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) + if((mAudioSession && !mAudioSession->isSpatial()) || (mNextAudioSession && !mNextAudioSession->isSpatial())) { // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; @@ -1394,13 +1252,13 @@ bool LLWebRTCVoiceClient::setSpatialChannel( } else { - return switchChannel(uri, true, false, false, "", parcel_local_id); + return switchChannel(uri, parcel_local_id == INVALID_PARCEL_ID ? sessionState::SESSION_TYPE_ESTATE : sessionState::SESSION_TYPE_PARCEL, parcel_local_id); } } void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) { - switchChannel(uuid.asString(), false, true, true); + switchChannel(uuid.asString(), sessionState::SESSION_TYPE_P2P); } void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) @@ -1419,9 +1277,7 @@ bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) sessionStatePtr_t session(findP2PSession(LLUUID(channelID))); if(session) { - session->mIsSpatial = false; - session->mReconnect = false; - session->mIsP2P = true; + session->mSessionType = sessionState::ESessionType::SESSION_TYPE_P2P; joinSession(session); return true; @@ -1604,7 +1460,7 @@ bool LLWebRTCVoiceClient::inSpatialChannel(void) if(mAudioSession) { - result = mAudioSession->mIsSpatial; + result = mAudioSession->isSpatial(); } return result; @@ -2061,34 +1917,12 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() return mAreaVoiceDisabled; } -void LLWebRTCVoiceClient::reapEmptySessions() -{ - sessionState::reapEmptySessions(); - - // mAudioSession or mNextAudioSession was reaped, - // so reset them. - if (mAudioSession && !sessionState::hasSession(mAudioSession->mChannelID)) - { - mAudioSession.reset(); - } - if (mNextAudioSession && !sessionState::hasSession(mNextAudioSession->mChannelID)) - { - mNextAudioSession.reset(); - } -} - //------------------------------------------------------------------------ std::map LLWebRTCVoiceClient::sessionState::mSessions; LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), - mIsChannel(false), - mIsSpatial(false), - mIsP2P(false), - mIncoming(false), - mVoiceActive(false), - mReconnect(false), mVolumeDirty(false), mMuteDirty(false), mParticipantsChanged(false) @@ -2102,8 +1936,7 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::crea sessionState::ptr_t session(new sessionState()); session->mChannelID = channelID; - connectionPtr_t connection = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); - session->mWebRTCConnections[channelID] = connection; + session->mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); session->mPrimaryConnectionID = channelID; @@ -2202,7 +2035,7 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pairmIsP2P) + if (result && result->mSessionType == sessionState::SESSION_TYPE_P2P) { return result; } @@ -2217,7 +2050,7 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() { for (auto &&connection : mWebRTCConnections) { - connection.second->shutDown(); + connection->shutDown(); } } @@ -2277,14 +2110,16 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) verifySessionState(); session->shutdownAllConnections(); // If this is the current audio session, clean up the pointer which will soon be dangling. - if(mAudioSession == session) + bool deleteAudioSession = mAudioSession == session; + bool deleteNextAudioSession = mNextAudioSession == session; + if (deleteAudioSession) { mAudioSession.reset(); mAudioSessionChanged = true; } // ditto for the next audio session - if(mNextAudioSession == session) + if (deleteNextAudioSession) { mNextAudioSession.reset(); } @@ -2696,73 +2531,68 @@ void LLVoiceWebRTCConnection::OnPeerShutDown() void LLVoiceWebRTCConnection::processIceUpdates() { - if (LLWebRTCVoiceClient::isShuttingDown()) - { - return; - } - if (mShutDown) - { - return; - } - LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); - if (!regionp || !regionp->capabilitiesReceived()) + if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) { - 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; - - 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); - bool iceCompleted = false; LLSD body; { if (!mTrickling) { - if (mIceCandidates.size()) + if (!mIceCandidates.empty() || mIceCompleted) { - LLSD candidates = LLSD::emptyArray(); - for (auto &ice_candidate : mIceCandidates) + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + if (!regionp || !regionp->capabilitiesReceived()) { - LLSD body_candidate; - body_candidate["sdpMid"] = ice_candidate.sdp_mid; - body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; - body_candidate["candidate"] = ice_candidate.candidate; - candidates.append(body_candidate); + LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; + return; } - body["candidates"] = candidates; - mIceCandidates.clear(); - } - else if (mIceCompleted) - { - LLSD body_candidate; - body_candidate["completed"] = true; - body["candidate"] = body_candidate; - iceCompleted = mIceCompleted; - mIceCompleted = false; - } - else - { - 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.sdp_mid; + body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; + body_candidate["candidate"] = ice_candidate.candidate; + 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; + } + + 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(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), + boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); + mOutstandingRequests++; + mTrickling = true; } - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( - url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), - boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); - mOutstandingRequests++; - mTrickling = true; } } } @@ -2862,7 +2692,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i } bool LLVoiceWebRTCConnection::connectionStateMachine() -{ +{ processIceUpdates(); switch (getVoiceConnectionState()) @@ -2872,6 +2702,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() if (mShutDown) { setVoiceConnectionState(VOICE_STATE_DISCONNECT); + break; } mTrickling = false; mIceCompleted = false; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f311f241dc..1e8ecdf5c2 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -330,7 +330,9 @@ public: bool isCallBackPossible(); bool isTextIMPossible(); - void processSessionStates(); + static void processSessionStates(); + + bool processConnectionStates(); void OnConnectionEstablished(const std::string &channelID); void OnConnectionFailure(const std::string &channelID); @@ -347,6 +349,15 @@ public: bool isEmpty() { return mWebRTCConnections.empty(); } + bool isSpatial() { return mSessionType == SESSION_TYPE_ESTATE || mSessionType == SESSION_TYPE_PARCEL; } + + typedef enum e_session_type + { + SESSION_TYPE_ESTATE = 1, + SESSION_TYPE_PARCEL, + SESSION_TYPE_P2P + } ESessionType; + std::string mHandle; std::string mGroupHandle; std::string mChannelID; @@ -362,9 +373,8 @@ public: LLUUID mIMSessionID; LLUUID mCallerID; int mErrorStatusCode; - bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) - bool mIsSpatial; // True for spatial channels - bool mIsP2P; + ESessionType mSessionType; + bool mIncoming; bool mVoiceActive; bool mReconnect; // Whether we should try to reconnect to this session if it's dropped @@ -381,16 +391,17 @@ public: LLUUID mVoiceFontID; static void VerifySessions(); - static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } + static bool hasSession(const std::string &sessionID) + { return mSessions.find(sessionID) != mSessions.end(); } private: - std::map mWebRTCConnections; - std::string mPrimaryConnectionID; + std::list mWebRTCConnections; + std::string mPrimaryConnectionID; + static std::map mSessions; // canonical list of outstanding sessions. sessionState(); - static std::map mSessions; // canonical list of outstanding sessions. static void for_eachPredicate(const std::pair &a, sessionFunc_t func); @@ -406,7 +417,6 @@ public: // Private Member Functions ////////////////////////////////////////////////////// - static void predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t &session); static void predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); static void predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); @@ -415,7 +425,6 @@ public: 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); - void reapEmptySessions(); ////////////////////////////// /// @name TVC/Server management and communication @@ -476,9 +485,6 @@ public: void verifySessionState(void); - void joinedAudioSession(const sessionStatePtr_t &session); - void leftAudioSession(const sessionStatePtr_t &session); - // 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); @@ -596,12 +602,8 @@ private: bool mIsInitialized; bool mShutdownComplete; - bool checkParcelChanged(bool update = false); bool switchChannel(const std::string channelID, - bool spatial = true, - bool no_reconnect = false, - bool is_p2p = false, - std::string hash = "", + sessionState::ESessionType sessionType, S32 parcel_local_id = INVALID_PARCEL_ID); void joinSession(const sessionStatePtr_t &session); @@ -808,6 +810,10 @@ protected: } EVoiceConnectionState getVoiceConnectionState() { + if (mVoiceStateMutex.isLocked()) + { + LL_WARNS("Voice") << "LOCKED." << LL_ENDL; + } LLMutexLock lock(&mVoiceStateMutex); return mVoiceConnectionState; } @@ -839,6 +845,7 @@ protected: llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; }; +#define VOICE_ELAPSED LLVoiceTimer(__FUNCTION__); #endif //LL_WebRTC_VOICE_CLIENT_H -- cgit v1.2.3 From ed3a1c57733b65c1738d7235f8696957a8eee36a Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Dec 2023 12:26:05 -0800 Subject: Connect to close neighboring regions and mute outgoing to them --- indra/newview/llvoicewebrtc.cpp | 300 +++++++++++++++++++++++----------------- indra/newview/llvoicewebrtc.h | 47 ++++--- 2 files changed, 199 insertions(+), 148 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9a1b3525d7..0ff8a09de8 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -85,6 +85,8 @@ extern LLMenuBarGL* gMenuBarView; extern void handle_voice_morphing_subscribe(); namespace { + + const F32 MAX_AUDIO_DIST = 50.0f; const F32 VOLUME_SCALE_WEBRTC = 0.01f; const F32 LEVEL_SCALE_WEBRTC = 0.008f; @@ -391,56 +393,35 @@ void LLWebRTCVoiceClient::updateSettings() ///////////////////////////// // session control messages -void LLWebRTCVoiceClient::predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) -{ - session->OnConnectionEstablished(channelID); -} - -void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) -{ - session->OnConnectionFailure(channelID); -} - -void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) -{ - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } - else if (mAudioSession && mAudioSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } - sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); -} - -void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID) +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID, const LLUUID& regionID) { - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) - { - mAudioSession = mNextAudioSession; - mNextAudioSession.reset(); - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - } - else if (mAudioSession && mAudioSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - } - sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); -} - -void LLWebRTCVoiceClient::sessionState::OnConnectionEstablished(const std::string& channelID) -{ - if (channelID == mPrimaryConnectionID) + if (gAgent.getRegion()->getRegionID() == regionID) { + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } } } -void LLWebRTCVoiceClient::sessionState::OnConnectionFailure(const std::string &channelID) +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID) { - if (channelID == mPrimaryConnectionID) + if (gAgent.getRegion()->getRegionID() == regionID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + mAudioSession = mNextAudioSession; + mNextAudioSession.reset(); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } } } @@ -457,6 +438,7 @@ void LLWebRTCVoiceClient::idle(void* user_data) void LLWebRTCVoiceClient::sessionState::processSessionStates() { + auto iter = mSessions.begin(); while (iter != mSessions.end()) { @@ -473,10 +455,20 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { - std::list::iterator iter = mWebRTCConnections.begin(); + // Estate voice requires connection to neighboring regions. + std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); + + std::list::iterator iter = mWebRTCConnections.begin(); while (iter != mWebRTCConnections.end()) { + if (neighbor_ids.find(iter->get()->getRegionID()) == neighbor_ids.end()) + { + // shut down connections to neighbors that are too far away. + iter->get()->shutDown(); + } + neighbor_ids.erase(iter->get()->getRegionID()); + if (!iter->get()->connectionStateMachine()) { iter = mWebRTCConnections.erase(iter); @@ -486,6 +478,15 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() ++iter; } } + + // add new connections for new neighbors + if (mSessionType == SESSION_TYPE_ESTATE) + { + for (auto &neighbor : neighbor_ids) + { + mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + } + } return !mWebRTCConnections.empty(); } @@ -505,26 +506,33 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } - - if (mVoiceEnabled && (!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) + bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); + if ((!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) { // check to see if parcel changed. - std::string channelID = regionp->getRegionID().asString(); + std::string channelID = "Estate"; + S32 parcel_local_id = INVALID_PARCEL_ID; - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - S32 parcel_local_id = INVALID_PARCEL_ID; - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + if (voiceEnabled) { - if (!parcel->getParcelFlagAllowVoice()) - { - channelID.clear(); - } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - parcel_local_id = parcel->getLocalID(); - channelID += "-" + std::to_string(parcel->getLocalID()); + if (!parcel->getParcelFlagAllowVoice()) + { + channelID.clear(); + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + parcel_local_id = parcel->getLocalID(); + channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + } } } + else + { + channelID.clear(); + } if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || (!mAudioSession && !channelID.empty()) || @@ -532,9 +540,13 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { setSpatialChannel(channelID, "", parcel_local_id); } + } + else + { + } sessionState::processSessionStates(); - if (mVoiceEnabled) + if (voiceEnabled) { updatePosition(); sendPositionAndVolumeUpdate(true); @@ -766,26 +778,6 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) if (mSpatialCoordsDirty || force) { Json::Value spatial = Json::objectValue; - LLVector3d earPosition; - LLQuaternion earRot; - switch (mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earRot = mAvatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earRot = mCameraRot; - break; - } spatial["sp"] = Json::objectValue; spatial["sp"]["x"] = (int) (mAvatarPosition[0] * 100); @@ -798,14 +790,14 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) spatial["sh"]["w"] = (int) (mAvatarRot[3] * 100); spatial["lp"] = Json::objectValue; - spatial["lp"]["x"] = (int) (earPosition[0] * 100); - spatial["lp"]["y"] = (int) (earPosition[1] * 100); - spatial["lp"]["z"] = (int) (earPosition[2] * 100); + 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) (earRot[0] * 100); - spatial["lh"]["y"] = (int) (earRot[1] * 100); - spatial["lh"]["z"] = (int) (earRot[2] * 100); - spatial["lh"]["w"] = (int) (earRot[3] * 100); + 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; if (force || (uint_audio_level != mAudioLevel)) @@ -1479,79 +1471,120 @@ std::string LLWebRTCVoiceClient::getAudioSessionURI() ///////////////////////////// // Sending updates of current state -void LLWebRTCVoiceClient::enforceTether(void) +void LLWebRTCVoiceClient::enforceTether() { - LLVector3d tethered = mCameraRequestedPosition; + LLVector3d tethered = mListenerRequestedPosition; // constrain 'tethered' to within 50m of mAvatarPosition. { - F32 max_dist = 50.0f; - LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; + LLVector3d camera_offset = mListenerRequestedPosition - mAvatarPosition; F32 camera_distance = (F32)camera_offset.magVec(); - if(camera_distance > max_dist) + if(camera_distance > MAX_AUDIO_DIST) { tethered = mAvatarPosition + - (max_dist / camera_distance) * camera_offset; + (MAX_AUDIO_DIST / camera_distance) * camera_offset; } } - if(dist_vec_squared(mCameraPosition, tethered) > 0.01) + if(dist_vec_squared(mListenerPosition, tethered) > 0.01) { - mCameraPosition = tethered; + mListenerPosition = tethered; mSpatialCoordsDirty = true; } } -void LLWebRTCVoiceClient::updatePosition(void) +void LLWebRTCVoiceClient::updateNeighboringRegions() { + 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), + 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()); + } + } +} + +void LLWebRTCVoiceClient::updatePosition(void) +{ LLViewerRegion *region = gAgent.getRegion(); if(region && isAgentAvatarValid()) { - LLVector3d pos; - LLQuaternion qrot; + 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 // 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 - pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); - - LLWebRTCVoiceClient::getInstance()->setCameraPosition( - pos, // position - LLVector3::zero, // velocity - LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix - - // Send the current avatar position to the voice code - qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); - pos = gAgentAvatarp->getPositionGlobal(); + LLVector3d earPosition; + LLQuaternion earRot; + switch (mEarLocation) + { + case earLocCamera: + default: + earPosition = region->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); + earRot = LLViewerCamera::getInstance()->getQuaternion(); + break; - // TODO: Can we get the head offset from outside the LLVOAvatar? - // pos += LLVector3d(mHeadOffset); - pos += LLVector3d(0.f, 0.f, 1.f); + 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 - LLWebRTCVoiceClient::getInstance()->setAvatarPosition( - pos, // position - LLVector3::zero, // velocity - qrot); // rotation matrix + setAvatarPosition( + avatar_pos, // position + LLVector3::zero, // velocity + avatar_qrot); // rotation matrix enforceTether(); + + if (mSpatialCoordsDirty) + { + updateNeighboringRegions(); + } } } -void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) +void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { - mCameraRequestedPosition = position; + + mListenerPosition = position; - if(mCameraVelocity != velocity) + if(mListenerVelocity != velocity) { - mCameraVelocity = velocity; + mListenerVelocity = velocity; mSpatialCoordsDirty = true; } - if(mCameraRot != rot) + if(mListenerRot != rot) { - mCameraRot = rot; + mListenerRot = rot; mSpatialCoordsDirty = true; } } @@ -1925,7 +1958,8 @@ LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), mVolumeDirty(false), mMuteDirty(false), - mParticipantsChanged(false) + mParticipantsChanged(false), + mShuttingDown(false) { } @@ -2047,7 +2081,8 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() -{ +{ + mShuttingDown = true; for (auto &&connection : mWebRTCConnections) { connection->shutDown(); @@ -2686,7 +2721,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); return; } - LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; + LL_WARNS("Voice") << "Unable to connect voice." << body << " RESULT: " << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; } @@ -2753,7 +2788,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() mWebRTCAudioInterface->setMute(mMuted); mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); mWebRTCAudioInterface->setSendVolume(mMicGain); - LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID); + LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_SESSION_UP); } break; @@ -2767,7 +2802,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } case VOICE_STATE_SESSION_RETRY: - LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID); + LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; break; @@ -2903,10 +2938,19 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url void LLVoiceWebRTCConnection::setMuteMic(bool muted) { - mMuted = true; + mMuted = muted; if (mWebRTCAudioInterface) { - mWebRTCAudioInterface->setMute(muted); + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp && mRegionID == regionp->getRegionID()) + { + mWebRTCAudioInterface->setMute(muted); + } + else + { + // always mute to regions the agent isn't on, to prevent echo. + mWebRTCAudioInterface->setMute(true); + } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 1e8ecdf5c2..9b8372cba7 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -242,8 +242,8 @@ public: void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {}; - void OnConnectionEstablished(const std::string& channelID); - void OnConnectionFailure(const std::string &channelID); + void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); + void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); void sendPositionAndVolumeUpdate(bool force); void updateOwnVolume(); @@ -312,6 +312,12 @@ public: typedef boost::weak_ptr wptr_t; typedef boost::function sessionFunc_t; + typedef enum e_session_type + { + SESSION_TYPE_ESTATE = 1, + SESSION_TYPE_PARCEL, + SESSION_TYPE_P2P + } ESessionType; static ptr_t createSession(const std::string& channelID, S32 parcel_local_id); ~sessionState(); @@ -334,9 +340,6 @@ public: bool processConnectionStates(); - void OnConnectionEstablished(const std::string &channelID); - void OnConnectionFailure(const std::string &channelID); - void sendData(const std::string &data); void setMuteMic(bool muted); @@ -351,12 +354,7 @@ public: bool isSpatial() { return mSessionType == SESSION_TYPE_ESTATE || mSessionType == SESSION_TYPE_PARCEL; } - typedef enum e_session_type - { - SESSION_TYPE_ESTATE = 1, - SESSION_TYPE_PARCEL, - SESSION_TYPE_P2P - } ESessionType; + ESessionType getSessionType() { return mSessionType; } std::string mHandle; std::string mGroupHandle; @@ -378,6 +376,7 @@ public: bool mIncoming; bool mVoiceActive; bool mReconnect; // Whether we should try to reconnect to this session if it's dropped + bool mShuttingDown; // Set to true when the volume/mute state of someone in the participant list changes. // The code will have to walk the list to find the changed participant(s). @@ -417,8 +416,6 @@ public: // Private Member Functions ////////////////////////////////////////////////////// - static void predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); - static void predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute); @@ -459,10 +456,13 @@ public: ///////////////////////////// // Sending updates of current state void updatePosition(void); - void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); + void setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); bool channelFromRegion(LLViewerRegion *region, std::string &name); + LLVector3d getListenerPosition() { return mListenerPosition; } + LLVector3d getSpeakerPosition() { return mAvatarPosition; } + void setEarLocation(S32 loc); @@ -613,23 +613,28 @@ private: std::string displayNameFromAvatar(LLVOAvatar *avatar); - bool inSpatialChannel(void); + bool inSpatialChannel(); std::string getAudioSessionURI(); void setHidden(bool hidden) override; //virtual - void enforceTether(void); + void enforceTether(); + + void updateNeighboringRegions(); + std::set getNeighboringRegions() { return mNeighboringRegions; } bool mSpatialCoordsDirty; - LLVector3d mCameraPosition; - LLVector3d mCameraRequestedPosition; - LLVector3 mCameraVelocity; - LLQuaternion mCameraRot; + LLVector3d mListenerPosition; + LLVector3d mListenerRequestedPosition; + LLVector3 mListenerVelocity; + LLQuaternion mListenerRot; LLVector3d mAvatarPosition; LLVector3 mAvatarVelocity; LLQuaternion mAvatarRot; + + std::set mNeighboringRegions; // includes current region bool mMuteMic; bool mMuteMicDirty; @@ -763,6 +768,8 @@ class LLVoiceWebRTCConnection : void setMicGain(F32 volume); void setSpeakerVolume(F32 volume); + LLUUID getRegionID() { return mRegionID; } + void shutDown() { LLMutexLock lock(&mVoiceStateMutex); -- cgit v1.2.3 From 52dc988519a3832194fe63a015fb1bdecf86fcf3 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Dec 2023 13:21:06 -0800 Subject: oopse, forgot to set the requested listener position --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0ff8a09de8..0c9c5c5bdd 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1574,7 +1574,7 @@ void LLWebRTCVoiceClient::updatePosition(void) void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { - mListenerPosition = position; + mListenerRequestedPosition = position; if(mListenerVelocity != velocity) { -- cgit v1.2.3 From 09ca8a07ea0aee8e43c657ecf7ac1158067f3cfc Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Dec 2023 18:37:34 -0800 Subject: add concept of primary/secondary connections --- indra/newview/llvoicewebrtc.cpp | 29 +++++++++++++++++++---------- indra/newview/llvoicewebrtc.h | 2 ++ 2 files changed, 21 insertions(+), 10 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0c9c5c5bdd..b3de360aa9 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -5,7 +5,7 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * + * ne * 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; @@ -506,6 +506,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } + mNeighboringRegions.insert(regionp->getRegionID()); bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); if ((!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) { @@ -540,10 +541,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { setSpatialChannel(channelID, "", parcel_local_id); } - } - else - { - } sessionState::processSessionStates(); if (voiceEnabled) @@ -822,7 +819,6 @@ void LLWebRTCVoiceClient::updateOwnVolume() { if (!mMuteMic && !mTuningMode) { audio_level = getAudioLevel(); - LL_WARNS("Voice") << "Level " << audio_level << LL_ENDL; } sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); @@ -2509,9 +2505,16 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } LLWebRTCVoiceClient::participantStatePtr_t participant = LLWebRTCVoiceClient::getInstance()->findParticipantByID(mChannelID, agent_id); - bool joined = voice_data[participant_id].get("j", Json::Value(false)).asBool(); + bool joined = false; + bool primary = false; + if (voice_data[participant_id].isMember("j")) + { + joined = true; + primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + } + new_participant |= joined; - if (!participant && joined) + if (!participant && joined && primary) { participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); } @@ -2541,9 +2544,15 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface { mWebRTCDataInterface = data_interface; mWebRTCDataInterface->setDataObserver(this); + Json::FastWriter writer; - Json::Value root = Json::objectValue; - root["j"] = true; + Json::Value root = Json::objectValue; + Json::Value join_obj = Json::objectValue; + if (gAgent.getRegion()->getRegionID() == mRegionID) + { + join_obj["p"] = true; + } + root["j"] = join_obj; std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 9b8372cba7..f89501e15c 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -843,6 +843,8 @@ protected: F32 mMicGain; F32 mSpeakerVolume; + bool mPrimary; + std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- cgit v1.2.3 From af5cc7bd7098ddeffd2ed4765ba433f3a1961644 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 24 Dec 2023 00:05:46 -0800 Subject: Don't need to send level data up to the server anymore --- indra/newview/llvoicewebrtc.cpp | 102 +++++++++++++++------------------------- indra/newview/llvoicewebrtc.h | 9 +--- 2 files changed, 39 insertions(+), 72 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b3de360aa9..99061c00da 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -508,45 +508,43 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } mNeighboringRegions.insert(regionp->getRegionID()); bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); - if ((!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) - { - // check to see if parcel changed. - std::string channelID = "Estate"; - S32 parcel_local_id = INVALID_PARCEL_ID; + // check to see if parcel changed. + std::string channelID = "Estate"; + S32 parcel_local_id = INVALID_PARCEL_ID; - if (voiceEnabled) + if (voiceEnabled) + { + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + if (!parcel->getParcelFlagAllowVoice()) { - if (!parcel->getParcelFlagAllowVoice()) - { - channelID.clear(); - } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) - { - parcel_local_id = parcel->getLocalID(); - channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); - } + channelID.clear(); + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + parcel_local_id = parcel->getLocalID(); + channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); } } - else - { - channelID.clear(); - } + } + else + { + channelID.clear(); + } - if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || - (!mAudioSession && !channelID.empty()) || - (mAudioSession && channelID != mAudioSession->mChannelID)) - { - setSpatialChannel(channelID, "", parcel_local_id); - } + if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || + (!mAudioSession && !mNextAudioSession && !channelID.empty()) || + (mAudioSession && channelID != mAudioSession->mChannelID)) + { + setSpatialChannel(channelID, "", parcel_local_id); } + sessionState::processSessionStates(); if (voiceEnabled) { updatePosition(); - sendPositionAndVolumeUpdate(true); + sendPositionUpdate(true); updateOwnVolume(); } } @@ -752,25 +750,14 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) } else { - sendPositionAndVolumeUpdate(true); + sendPositionUpdate(true); } } -void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) +void LLWebRTCVoiceClient::sendPositionUpdate(bool force) { Json::FastWriter writer; std::string spatial_data; - std::string volume_data; - - F32 audio_level = 0.0; - uint32_t uint_audio_level = 0.0; - - if (!mMuteMic && !mTuningMode) - { - audio_level = getAudioLevel(); - uint_audio_level = (uint32_t) (audio_level*128); - - } if (mSpatialCoordsDirty || force) { @@ -797,21 +784,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) spatial["lh"]["w"] = (int) (mListenerRot[3] * 100); mSpatialCoordsDirty = false; - if (force || (uint_audio_level != mAudioLevel)) - { - spatial["p"] = uint_audio_level; - } spatial_data = writer.write(spatial); - } - if (force || (uint_audio_level != mAudioLevel)) - { - Json::Value volume = Json::objectValue; - volume["p"] = uint_audio_level; - volume_data = writer.write(volume); - } - mAudioLevel = uint_audio_level; - sessionState::for_each(boost::bind(predSendData, _1, spatial_data, volume_data)); + sessionState::for_each(boost::bind(predSendData, _1, spatial_data)); + } } void LLWebRTCVoiceClient::updateOwnVolume() { @@ -834,16 +810,12 @@ void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::session } } -void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data, const std::string& volume_data) +void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data) { if (session->isSpatial() && !spatial_data.empty()) { session->sendData(spatial_data); } - else if (!volume_data.empty()) - { - session->sendData(volume_data); - } } void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) @@ -966,6 +938,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad // participant isn't already in one list or the other. result.reset(new participantState(agent_id)); mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); + mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result)); mParticipantsChanged = true; result->mAvatarIDValid = true; @@ -975,8 +948,6 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad { mMuteDirty = true; } - - mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) { @@ -1682,7 +1653,8 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool 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", @@ -1967,8 +1939,6 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::crea sessionState::ptr_t session(new sessionState()); session->mChannelID = channelID; session->mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); - - session->mPrimaryConnectionID = channelID; // add agent as participant session->addParticipant(gAgentID); @@ -2548,7 +2518,8 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface Json::FastWriter writer; Json::Value root = Json::objectValue; Json::Value join_obj = Json::objectValue; - if (gAgent.getRegion()->getRegionID() == mRegionID) + LLUUID regionID = gAgent.getRegion()->getRegionID(); + if (regionID == mRegionID) { join_obj["p"] = true; } @@ -2868,6 +2839,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); return false; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f89501e15c..f042ecca85 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -244,7 +244,7 @@ public: void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); - void sendPositionAndVolumeUpdate(bool force); + void sendPositionUpdate(bool force); void updateOwnVolume(); ////////////////////////////// @@ -396,7 +396,6 @@ public: private: std::list mWebRTCConnections; - std::string mPrimaryConnectionID; static std::map mSessions; // canonical list of outstanding sessions. sessionState(); @@ -416,7 +415,7 @@ public: // Private Member Functions ////////////////////////////////////////////////////// - static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); + 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); @@ -597,8 +596,6 @@ private: LLVoiceDeviceList mCaptureDevices; LLVoiceDeviceList mRenderDevices; - uint32_t mAudioLevel; - bool mIsInitialized; bool mShutdownComplete; @@ -843,8 +840,6 @@ protected: F32 mMicGain; F32 mSpeakerVolume; - bool mPrimary; - std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- cgit v1.2.3 From 28b3e530821f06df6c447e139bae57a976f10b7e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 24 Dec 2023 20:50:08 -0800 Subject: Add viewer-visible session ID to allow multiple sessions under same agent id --- indra/newview/llvoicewebrtc.cpp | 21 +++++++++++++-------- indra/newview/llvoicewebrtc.h | 2 ++ 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 99061c00da..305868e415 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -414,6 +414,10 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, { if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) { + if (mAudioSession) + { + mAudioSession->shutdownAllConnections(); + } mAudioSession = mNextAudioSession; mNextAudioSession.reset(); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); @@ -2593,6 +2597,8 @@ void LLVoiceWebRTCConnection::processIceUpdates() iceCompleted = mIceCompleted; mIceCompleted = false; } + + body["viewer_session"] = mViewerSession; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( @@ -2663,9 +2669,10 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result } LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); - if (result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) + 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 { @@ -2673,13 +2680,9 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result mOutstandingRequests--; return; } - std::string voiceAccountServerUri; - std::string voiceUserName = gAgent.getID().asString(); - std::string voicePassword = ""; // no password for now. LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" - << " user " << (voiceUserName.empty() ? "not set" : "set") << " password " - << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << mRemoteChannelSDP << LL_ENDL; + << " channel sdp " << mRemoteChannelSDP << LL_ENDL; mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); mOutstandingRequests--; @@ -2819,8 +2822,9 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } -void LLVoiceWebRTCConnection::sendData(const std::string& data) { - if (mWebRTCDataInterface) +void LLVoiceWebRTCConnection::sendData(const std::string& data) { + + if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) { mWebRTCDataInterface->sendData(data, false); } @@ -2858,6 +2862,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); LLSD body; body["logout"] = TRUE; + body["viewer_session"] = mViewerSession; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f042ecca85..c8ba3ee115 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -829,6 +829,8 @@ protected: std::string mChannelSDP; std::string mRemoteChannelSDP; + LLUUID mViewerSession; + std::string mChannelID; LLUUID mRegionID; S32 mParcelLocalID; -- cgit v1.2.3 From 1cdc25750c86c36ebad7cca711217a3cf8a30a8e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 25 Dec 2023 17:41:48 -0800 Subject: Fix enable/disable issue when connecting to multiple regions --- indra/newview/llvoicewebrtc.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 305868e415..0c15dbccaf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -362,6 +362,7 @@ void LLWebRTCVoiceClient::cleanUp() mNextAudioSession.reset(); mAudioSession.reset(); + mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -510,7 +511,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } - mNeighboringRegions.insert(regionp->getRegionID()); bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); // check to see if parcel changed. std::string channelID = "Estate"; @@ -1215,6 +1215,7 @@ bool LLWebRTCVoiceClient::setSpatialChannel( } else { + mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); return switchChannel(uri, parcel_local_id == INVALID_PARCEL_ID ? sessionState::SESSION_TYPE_ESTATE : sessionState::SESSION_TYPE_PARCEL, parcel_local_id); } } @@ -1535,10 +1536,7 @@ void LLWebRTCVoiceClient::updatePosition(void) enforceTether(); - if (mSpatialCoordsDirty) - { - updateNeighboringRegions(); - } + updateNeighboringRegions(); } } @@ -2843,7 +2841,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); return false; } -- cgit v1.2.3 From 1043accd9653022d138ea579968e6da65ea0ff3c Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 2 Jan 2024 15:28:31 -0800 Subject: Set mute, speaker volume, mic gain on creation of new connection. --- indra/newview/llvoicewebrtc.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0c15dbccaf..dd18fabf47 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -358,8 +358,6 @@ void LLWebRTCVoiceClient::terminate() void LLWebRTCVoiceClient::cleanUp() { - LL_DEBUGS("Voice") << LL_ENDL; - mNextAudioSession.reset(); mAudioSession.reset(); mNeighboringRegions.clear(); @@ -489,7 +487,10 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { for (auto &neighbor : neighbor_ids) { - mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connection->setMicGain(mMicGain); + connection->setMuteMic(mMuted); + connection->setSpeakerVolume(mSpeakerVolume); } } return !mWebRTCConnections.empty(); @@ -2494,7 +2495,10 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) { - LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); + if (agent_id != gAgentID) + { + LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); + } } else { -- cgit v1.2.3 From 45fea8ef3f548fa46da29a8fd06a7fc9807924b7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 3 Jan 2024 20:33:01 -0800 Subject: some comments --- indra/newview/llvoicewebrtc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index c8ba3ee115..bf5c68ed2b 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -364,9 +364,9 @@ public: std::string mErrorStatusString; std::queue mTextMsgQueue; - bool mMuted; - F32 mMicGain; - F32 mSpeakerVolume; + bool mMuted; // this session is muted. + F32 mMicGain; // gain for this session. + F32 mSpeakerVolume; // volume for this session. LLUUID mIMSessionID; LLUUID mCallerID; -- cgit v1.2.3 From 9b2362f73ee5796279acc717fcc0bcc138f6519f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 4 Jan 2024 13:02:55 -0800 Subject: Enable AEC --- indra/llwebrtc/llwebrtc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 9b3ec2889b..8e56f9c222 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -110,7 +110,7 @@ void LLWebRTCImpl::init() mTaskQueueFactory.get(), std::unique_ptr(mTuningAudioDeviceObserver)); mTuningDeviceModule->Init(); - mTuningDeviceModule->SetStereoRecording(false); + mTuningDeviceModule->SetStereoRecording(true); mTuningDeviceModule->SetStereoPlayout(true); mTuningDeviceModule->EnableBuiltInAEC(false); updateDevices(); @@ -118,7 +118,7 @@ void LLWebRTCImpl::init() rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); webrtc::AudioProcessing::Config apm_config; - apm_config.echo_canceller.enabled = false; + 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; @@ -141,7 +141,7 @@ void LLWebRTCImpl::init() mPeerDeviceModule->Init(); mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); - mPeerDeviceModule->SetStereoRecording(false); + mPeerDeviceModule->SetStereoRecording(true); mPeerDeviceModule->SetStereoPlayout(true); mPeerDeviceModule->EnableBuiltInAEC(false); mPeerDeviceModule->InitMicrophone(); @@ -515,7 +515,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() cricket::AudioOptions audioOptions; audioOptions.auto_gain_control = true; - audioOptions.echo_cancellation = false; // incompatible with opus stereo + audioOptions.echo_cancellation = true; // incompatible with opus stereo audioOptions.noise_suppression = true; mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); @@ -878,7 +878,7 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface * 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\n"; + << " minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1;maxplaybackrate=48000;sprop-maxcapturerate=48000;sprop-maxplaybackrate=48000\n"; } else { -- cgit v1.2.3 From 106a589dee7bd91f8c8893e1f077a25efb7e83eb Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 9 Jan 2024 11:57:01 -0800 Subject: New WebRTC with echo cancellation fix. Also, start/stop recording depending on whether WebRTC has negotiated. --- indra/cmake/WebRTC.cmake | 6 ++-- indra/llwebrtc/llwebrtc.cpp | 74 ++++++++++++++++++++++++++++++++++-------- indra/llwebrtc/llwebrtc_impl.h | 2 ++ 3 files changed, 65 insertions(+), 17 deletions(-) (limited to 'indra') diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 1c32607766..2878d7dd88 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -7,7 +7,7 @@ if (WINDOWS) FetchContent_Declare( webrtc URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86.tar.bz2" - URL_HASH "MD5=0d55e58efceed3fb48085a5f0c58881c" + URL_HASH "MD5=cefbd446b1b152ac08217fc78648fb99" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" @@ -16,7 +16,7 @@ if (WINDOWS) FetchContent_Declare( webrtc URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.windows_x86_64.tar.bz2" - URL_HASH "MD5=dfb692562770dc8c877ebfe4302e2881" + URL_HASH "MD5=b7a93b111e51ebcda21701c009c0676c" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" @@ -26,7 +26,7 @@ elseif (DARWIN) FetchContent_Declare( webrtc URL "https://webrtc-build-releases.s3.us-west-2.amazonaws.com/webrtc.macos_x86_64.tar.bz2" - URL_HASH "MD5=cfbcac7da897a862f9791ea29330b814" + URL_HASH "MD5=a965974e1d9fc7f55b852a8ff8ccf9a9" FIND_PACKAGE_ARGS NAMES webrtc DOWNLOAD_EXTRACT_TIMESTAMP TRUE DOWNLOAD_DIR "${LIBS_PREBUILT_DIR}/webrtc/" diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 8e56f9c222..fca490e8c2 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -150,7 +150,34 @@ void LLWebRTCImpl::init() mPeerDeviceModule->InitPlayout(); }); - apm->ApplyConfig(apm_config); + rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); + 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(), @@ -161,11 +188,28 @@ void LLWebRTCImpl::init() nullptr /* video_decoder_factory */, nullptr /* audio_mixer */, apm); + + mWorkerThread->BlockingCall( [this]() { mPeerDeviceModule->StartPlayout(); - mPeerDeviceModule->StartRecording(); + }); +} + +void LLWebRTCImpl::setRecording(bool recording) +{ + mWorkerThread->PostTask( + [this, recording]() + { + if (recording) + { + mPeerDeviceModule->StartRecording(); + } + else + { + mPeerDeviceModule->StopRecording(); + } }); } @@ -263,8 +307,6 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id) { mPeerDeviceModule->StopRecording(); } - - mPeerDeviceModule->StopRecording(); mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); mPeerDeviceModule->InitMicrophone(); mPeerDeviceModule->InitRecording(); @@ -427,6 +469,10 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnection * peer_connection) (*it)->terminate(); mPeerConnections.erase(it); } + if (mPeerConnections.empty()) + { + setRecording(false); + } } // @@ -519,6 +565,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() audioOptions.noise_suppression = true; mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); + rtc::scoped_refptr audio_track( mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(audioOptions).get())); audio_track->set_enabled(true); @@ -750,15 +797,14 @@ void LLWebRTCPeerConnectionImpl::OnConnectionChange(webrtc::PeerConnectionInterf { case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected: { - if (new_state == webrtc::PeerConnectionInterface::PeerConnectionState::kConnected) - { - mWebRTCImpl->PostWorkerTask([this]() { - for (auto &observer : mSignalingObserverList) - { - observer->OnAudioEstablished(this); - } - }); - } + mWebRTCImpl->setRecording(true); + + mWebRTCImpl->PostWorkerTask([this]() { + for (auto &observer : mSignalingObserverList) + { + observer->OnAudioEstablished(this); + } + }); break; } case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: @@ -872,8 +918,8 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface * // force mono down, stereo up if (std::sscanf(sdp_line.c_str(), "a=rtpmap:%i opus/%i/2", &payload_id, &bandwidth) == 2) { - sdp_mangled_stream << sdp_line << "\n"; 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) { diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 6a84f67ef5..884e107527 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -162,6 +162,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface LLWebRTCPeerConnection * newPeerConnection(); void freePeerConnection(LLWebRTCPeerConnection * peer_connection); + void setRecording(bool recording); + protected: std::unique_ptr mNetworkThread; -- cgit v1.2.3 From 844b2d8c8a916fde4bbdcc8dce4cd738a965f38e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 12 Jan 2024 17:11:39 -0800 Subject: checkpoint for adhoc voice --- indra/newview/llvoicewebrtc.cpp | 637 ++++++++++++++++++++-------------------- indra/newview/llvoicewebrtc.h | 146 ++++++--- 2 files changed, 422 insertions(+), 361 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index dd18fabf47..0f42c8ea8a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -246,9 +246,9 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mDevicesListUpdated(false), mAreaVoiceDisabled(false), - mAudioSession(), // TBD - should be NULL - mAudioSessionChanged(false), - mNextAudioSession(), + mSession(), // TBD - should be NULL + mSessionChanged(false), + mNextSession(), mCurrentParcelLocalID(0), @@ -358,8 +358,8 @@ void LLWebRTCVoiceClient::terminate() void LLWebRTCVoiceClient::cleanUp() { - mNextAudioSession.reset(); - mAudioSession.reset(); + mNextSession.reset(); + mSession.reset(); mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; @@ -396,11 +396,11 @@ void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID, cons { if (gAgent.getRegion()->getRegionID() == regionID) { - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + if (mNextSession && mNextSession->mChannelID == channelID) { LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); } - else if (mAudioSession && mAudioSession->mChannelID == channelID) + else if (mSession && mSession->mChannelID == channelID) { LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); } @@ -411,17 +411,17 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, { if (gAgent.getRegion()->getRegionID() == regionID) { - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + if (mNextSession && mNextSession->mChannelID == channelID) { - if (mAudioSession) + if (mSession) { - mAudioSession->shutdownAllConnections(); + mSession->shutdownAllConnections(); } - mAudioSession = mNextAudioSession; - mNextAudioSession.reset(); + mSession = mNextSession; + mNextSession.reset(); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } - else if (mAudioSession && mAudioSession->mChannelID == channelID) + else if (mSession && mSession->mChannelID == channelID) { LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } @@ -458,20 +458,9 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { - - // Estate voice requires connection to neighboring regions. - std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); - std::list::iterator iter = mWebRTCConnections.begin(); while (iter != mWebRTCConnections.end()) { - if (neighbor_ids.find(iter->get()->getRegionID()) == neighbor_ids.end()) - { - // shut down connections to neighbors that are too far away. - iter->get()->shutDown(); - } - neighbor_ids.erase(iter->get()->getRegionID()); - if (!iter->get()->connectionStateMachine()) { iter = mWebRTCConnections.erase(iter); @@ -481,19 +470,62 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() ++iter; } } + return !mWebRTCConnections.empty(); +} - // add new connections for new neighbors - if (mSessionType == SESSION_TYPE_ESTATE) + +////////////////////////// +// LLWebRTCVoiceClient::estateSessionState + +LLWebRTCVoiceClient::estateSessionState::estateSessionState() +{ + 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) +{ + 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) +{ +} + +bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() +{ + + // Estate voice requires connection to neighboring regions. + std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); + + for (auto& connection : mWebRTCConnections) { - for (auto &neighbor : neighbor_ids) + boost::shared_ptr spatialConnection = + boost::static_pointer_cast(connection); + + LLUUID regionID = spatialConnection.get()->getRegionID(); + + if (neighbor_ids.find(regionID) == neighbor_ids.end()) { - connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); - connection->setMicGain(mMicGain); - connection->setMuteMic(mMuted); - connection->setSpeakerVolume(mSpeakerVolume); + // shut down connections to neighbors that are too far away. + spatialConnection.get()->shutDown(); } + neighbor_ids.erase(regionID); } - return !mWebRTCConnections.empty(); + + // add new connections for new neighbors + for (auto &neighbor : neighbor_ids) + { + connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connection->setMicGain(mMicGain); + connection->setMuteMic(mMuted); + connection->setSpeakerVolume(mSpeakerVolume); + } + return LLWebRTCVoiceClient::sessionState::processConnectionStates(); } void LLWebRTCVoiceClient::voiceConnectionCoro() @@ -506,49 +538,75 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() while (!sShuttingDown) { llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - // add session for region or parcel voice. - LLViewerRegion *regionp = gAgent.getRegion(); - if (!regionp || regionp->getRegionID().isNull()) + bool voiceEnabled = mVoiceEnabled; + + if (!isAgentAvatarValid()) { continue; } - bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); - // check to see if parcel changed. - std::string channelID = "Estate"; - S32 parcel_local_id = INVALID_PARCEL_ID; - if (voiceEnabled) + if (inSpatialChannel()) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + // add session for region or parcel voice. + LLViewerRegion *regionp = gAgent.getRegion(); + if (!regionp || regionp->getRegionID().isNull()) + { + continue; + } + + updatePosition(); + + voiceEnabled = voiceEnabled && regionp->isVoiceEnabled(); + + if (voiceEnabled) { - if (!parcel->getParcelFlagAllowVoice()) + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // check to see if parcel changed. + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - channelID.clear(); + // parcel voice + if (!parcel->getParcelFlagAllowVoice()) + { + voiceEnabled = false; + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + S32 parcel_local_id = parcel->getLocalID(); + std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + + if (!inOrJoiningChannel(channelID)) + { + startParcelSession(channelID, parcel_local_id); + } + } + else + { + // parcel using estate voice + if (!inEstateChannel()) + { + startEstateSession(); + } + } } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + else { - parcel_local_id = parcel->getLocalID(); - channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + // estate voice + if (!inEstateChannel()) + { + startEstateSession(); + } } } - } - else - { - channelID.clear(); - } - - if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || - (!mAudioSession && !mNextAudioSession && !channelID.empty()) || - (mAudioSession && channelID != mAudioSession->mChannelID)) - { - setSpatialChannel(channelID, "", parcel_local_id); + else + { + // voice is disabled, so leave and disable PTT + leaveChannel(true); + } } sessionState::processSessionStates(); if (voiceEnabled) { - updatePosition(); sendPositionUpdate(true); updateOwnVolume(); } @@ -591,9 +649,9 @@ void LLWebRTCVoiceClient::requestRelog() void LLWebRTCVoiceClient::leaveAudioSession() { - if(mAudioSession) + if(mSession) { - LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mChannelID << LL_ENDL; + LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL; } else { @@ -866,11 +924,11 @@ void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) { - if(session == mAudioSession) + if(session == mSession) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the current session)" << LL_ENDL; } - else if(session == mNextAudioSession) + else if(session == mNextSession) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the next session)" << LL_ENDL; } @@ -891,17 +949,17 @@ void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) void LLWebRTCVoiceClient::muteListChanged() { // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. - if(mAudioSession) + if(mSession) { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + participantMap::iterator iter = mSession->mParticipantsByURI.begin(); - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + for(; iter != mSession->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; + mSession->mVolumeDirty = true; } } } @@ -1031,32 +1089,13 @@ void LLWebRTCVoiceClient::sessionState::removeAllParticipants() } } -/*static*/ -void LLWebRTCVoiceClient::sessionState::VerifySessions() -{ - return; - /* - std::map::iterator it = mSessions.begin(); - while (it != mSessions.end()) - { - if ((*it).second.expired()) - { - LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; - it = mSessions.erase(it); - } - else - ++it; - } - */ -} - void LLWebRTCVoiceClient::getParticipantList(std::set &participants) { - if(mAudioSession) + if(mSession) { - for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); - iter != mAudioSession->mParticipantsByUUID.end(); + for(participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin(); + iter != mSession->mParticipantsByUUID.end(); iter++) { participants.insert(iter->first); @@ -1066,9 +1105,9 @@ void LLWebRTCVoiceClient::getParticipantList(std::set &participants) bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) { - if(mAudioSession) + if(mSession) { - return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); + return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end()); } return false; } @@ -1139,54 +1178,32 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co } } -bool LLWebRTCVoiceClient::switchChannel(const std::string channelID, - sessionState::ESessionType session_type, - S32 parcel_local_id) +bool LLWebRTCVoiceClient::startEstateSession() { - if (mAudioSession) - { - // If we're already in a channel, or if we're joining one, terminate - // so we can rejoin with the new session data. - sessionTerminate(); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - deleteSession(mAudioSession); - } - - if (channelID.empty()) - { - // Leave any channel we may be in - LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + leaveChannel(false); + mNextSession = addSession("Estate", sessionState::ptr_t(new estateSessionState())); + return true; +} - if (mNextAudioSession) - { - deleteSession(mNextAudioSession); - } - // If voice was on, turn it off - if (LLVoiceClient::getInstance()->getUserPTTState()) - { - LLVoiceClient::getInstance()->setUserPTTState(false); - } +bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 parcelID) +{ + leaveChannel(false); + mNextSession = addSession(channelID, sessionState::ptr_t(new parcelSessionState(channelID, parcelID))); + return true; +} - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - } - else - { - if (mNextAudioSession) - { - deleteSession(mNextAudioSession); - } - LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; - mNextAudioSession = addSession(channelID, parcel_local_id); - mNextAudioSession->mSessionType = session_type; - } - return true; +bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID) +{ + leaveChannel(false); + mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID))); + return true; } void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) { - mNextAudioSession = session; + mNextSession = session; - if (mAudioSession) + if (mSession) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. @@ -1194,36 +1211,9 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::setNonSpatialChannel( - const std::string &uri, - const std::string &credentials) -{ - switchChannel(uri, sessionState::SESSION_TYPE_P2P); -} - -bool LLWebRTCVoiceClient::setSpatialChannel( - const std::string &uri, const std::string &credentials, S32 parcel_local_id) -{ - mAreaVoiceDisabled = uri.empty(); - - LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - - if((mAudioSession && !mAudioSession->isSpatial()) || (mNextAudioSession && !mNextAudioSession->isSpatial())) - { - // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. - LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; - return false; - } - else - { - mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); - return switchChannel(uri, parcel_local_id == INVALID_PARCEL_ID ? sessionState::SESSION_TYPE_ESTATE : sessionState::SESSION_TYPE_PARCEL, parcel_local_id); - } -} - void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) { - switchChannel(uuid.asString(), sessionState::SESSION_TYPE_P2P); + startAdHocSession(uuid.asString()); } void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) @@ -1242,9 +1232,7 @@ bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) sessionStatePtr_t session(findP2PSession(LLUUID(channelID))); if(session) { - session->mSessionType = sessionState::ESessionType::SESSION_TYPE_P2P; - - joinSession(session); + startAdHocSession(channelID); return true; } @@ -1309,14 +1297,12 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel() LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; // Make sure we don't rejoin the current session. - sessionStatePtr_t oldNextSession(mNextAudioSession); - mNextAudioSession.reset(); + sessionStatePtr_t oldNextSession(mNextSession); + mNextSession.reset(); // Most likely this will still be the current session at this point, but check it anyway. reapSession(oldNextSession); - verifySessionState(); - sessionTerminate(); } @@ -1419,13 +1405,23 @@ std::string LLWebRTCVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) return avatar->getFullname(); } -bool LLWebRTCVoiceClient::inSpatialChannel(void) +bool LLWebRTCVoiceClient::inOrJoiningChannel(const std::string& channelID) { - bool result = false; + 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(mAudioSession) + if(mSession) { - result = mAudioSession->isSpatial(); + result = mSession->isSpatial(); } return result; @@ -1435,8 +1431,8 @@ std::string LLWebRTCVoiceClient::getAudioSessionURI() { std::string result; - if(mAudioSession) - result = mAudioSession->mChannelID; + if(mSession) + result = mSession->mChannelID; return result; } @@ -1599,14 +1595,29 @@ bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string return result; } -void LLWebRTCVoiceClient::leaveChannel(void) +void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) { - if (mAudioSession || mNextAudioSession) - { - LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; - mChannelName.clear(); - sessionTerminate(); - } + mChannelName.clear(); + + if (mSession) + { + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + deleteSession(mSession); + } + + if (mNextSession) + { + deleteSession(mNextSession); + } + + // If voice was on, turn it off + if (stopTalking && LLVoiceClient::getInstance()->getUserPTTState()) + { + LLVoiceClient::getInstance()->setUserPTTState(false); + } } void LLWebRTCVoiceClient::setMuteMic(bool muted) @@ -1748,11 +1759,11 @@ void LLWebRTCVoiceClient::setMicGain(F32 gain) BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return FALSE; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { // I'm not sure what the semantics of this should be. @@ -1766,11 +1777,11 @@ BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) { std::string result; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mDisplayName; @@ -1784,11 +1795,11 @@ std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mIsSpeaking; @@ -1800,11 +1811,11 @@ BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mIsModeratorMuted; @@ -1816,11 +1827,11 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { F32 result = 0; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if (participant) { result = participant->mLevel; @@ -1831,11 +1842,11 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { // I'm not sure what the semantics of this should be. @@ -1850,7 +1861,7 @@ BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id) { BOOL result = FALSE; - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mOnMuteList; @@ -1865,7 +1876,7 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) // Minimum volume will be returned for users with voice disabled F32 result = LLVoiceClient::VOLUME_MIN; - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mVolume; @@ -1879,9 +1890,9 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { - if(mAudioSession) + if(mSession) { - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if (participant && !participant->mIsSelf) { if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) @@ -1898,7 +1909,7 @@ void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) participant->mVolume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); participant->mVolumeDirty = true; - mAudioSession->mVolumeDirty = true; + mSession->mVolumeDirty = true; } } } @@ -1907,7 +1918,7 @@ std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) { std::string result; - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mGroupID; @@ -1935,20 +1946,12 @@ LLWebRTCVoiceClient::sessionState::sessionState() : } /*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession(const std::string& channelID, S32 parcelLocalID) +void LLWebRTCVoiceClient::sessionState::addSession( + const std::string &channelID, + LLWebRTCVoiceClient::sessionState::ptr_t& session) { - LLUUID region_id = gAgent.getRegion()->getRegionID(); - - sessionState::ptr_t session(new sessionState()); - session->mChannelID = channelID; - session->mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); - - // add agent as participant session->addParticipant(gAgentID); - mSessions[channelID] = session; - - return session; } LLWebRTCVoiceClient::sessionState::~sessionState() @@ -2038,7 +2041,7 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pairmSessionType == sessionState::SESSION_TYPE_P2P) + if (result && !result->isSpatial()) { return result; } @@ -2059,48 +2062,32 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string& channel_id, S32 parcel_local_id) +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionStatePtr_t &session) { - sessionStatePtr_t result; - - // Check whether there's already a session with this URI - result = sessionState::matchSessionByChannelID(channel_id); - - if(!result) + sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id); + if (!existingSession) { // No existing session found. LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; - result = sessionState::createSession(channel_id, parcel_local_id); - result->setMuteMic(mMuteMic); - result->setMicGain(mMicGain); - result->setSpeakerVolume(mSpeakerVolume); + session->setMuteMic(mMuteMic); + session->setMicGain(mMicGain); + session->setSpeakerVolume(mSpeakerVolume); if (LLVoiceClient::instance().getVoiceEffectEnabled()) { - result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); + session->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); } + + sessionState::addSession(channel_id, session); + return session; } else { // Found an existing session - - if (channel_id != result->mChannelID) - { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing uri from " << result->mChannelID << " to " << channel_id << LL_ENDL; - - result->mChannelID = channel_id; - - verifySessionState(); - } - - LL_DEBUGS("Voice") << "returning existing session: CHANNEL " << channel_id << LL_ENDL; - } - - verifySessionState(); - - return result; + LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL; + return existingSession; + } } void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) @@ -2111,29 +2098,23 @@ void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::session void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) { // At this point, the session should be unhooked from all lists and all state should be consistent. - verifySessionState(); session->shutdownAllConnections(); // If this is the current audio session, clean up the pointer which will soon be dangling. - bool deleteAudioSession = mAudioSession == session; - bool deleteNextAudioSession = mNextAudioSession == session; + bool deleteAudioSession = mSession == session; + bool deleteNextAudioSession = mNextSession == session; if (deleteAudioSession) { - mAudioSession.reset(); - mAudioSessionChanged = true; + mSession.reset(); + mSessionChanged = true; } // ditto for the next audio session if (deleteNextAudioSession) { - mNextAudioSession.reset(); + mNextSession.reset(); } } -void LLWebRTCVoiceClient::verifySessionState(void) -{ - sessionState::VerifySessions(); -} - void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) { mParticipantObservers.insert(observer); @@ -2170,14 +2151,14 @@ void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) { LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" - << " mAudioSession=" << mAudioSession + << " mSession=" << mSession << LL_ENDL; - if(mAudioSession) + if(mSession) { if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) { - switch(mAudioSession->mErrorStatusCode) + switch(mSession->mErrorStatusCode) { case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; @@ -2193,11 +2174,11 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt } // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; + mSession->mErrorStatusCode = 0; } else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) { - switch(mAudioSession->mErrorStatusCode) + switch(mSession->mErrorStatusCode) { case HTTP_NOT_FOUND: // NOT_FOUND // *TODO: Should this be 503? @@ -2208,7 +2189,7 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; + mSession->mErrorStatusCode = 0; break; } } @@ -2315,10 +2296,63 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } +///////////////////////////// +// LLVoiceWebRTCConnection + +LLVoiceWebRTCConnection::LLVoiceWebRTCConnection() : + mWebRTCAudioInterface(nullptr), + mWebRTCPeerConnection(nullptr), + mMuted(true), + mSpeakerVolume(0.0), + mMicGain(0.0) +{ + +} + +LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + // peer connection and observers will be cleaned up + // by llwebrtc::terminate() on shutdown. + return; + } + llwebrtc::freePeerConnection(mWebRTCPeerConnection); + mWebRTCPeerConnection = nullptr; +} + + +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); + } +} + ///////////////////////////// // WebRTC Signaling Handlers -LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string& channelID) : +LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID) : mWebRTCPeerConnection(nullptr), mWebRTCAudioInterface(nullptr), mWebRTCDataInterface(nullptr), @@ -2329,16 +2363,13 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 par mRegionID(regionID), mParcelLocalID(parcelLocalID), mShutDown(false), - mOutstandingRequests(0), - mMuted(true), - mSpeakerVolume(0.0), - mMicGain(0.0) + mOutstandingRequests(0) { mWebRTCPeerConnection = llwebrtc::newPeerConnection(); mWebRTCPeerConnection->setSignalingObserver(this); } -LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() +LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2348,11 +2379,9 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() } assert(mOutstandingRequests == 0); mWebRTCPeerConnection->unsetSignalingObserver(this); - llwebrtc::freePeerConnection(mWebRTCPeerConnection); - mWebRTCPeerConnection = nullptr; } -void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +void LLVoiceWebRTCSpatialConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; @@ -2374,13 +2403,13 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs } } -void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +void LLVoiceWebRTCSpatialConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { LLMutexLock lock(&mVoiceStateMutex); mIceCandidates.push_back(candidate); } -void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2390,7 +2419,7 @@ void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD mOutstandingRequests--; } -void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2406,8 +2435,8 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS 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)); + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateComplete, this, ice_completed, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); return; } @@ -2421,7 +2450,7 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS mOutstandingRequests--; } -void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) +void LLVoiceWebRTCSpatialConnection::OnOfferAvailable(const std::string &sdp) { LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; LLMutexLock lock(&mVoiceStateMutex); @@ -2432,14 +2461,14 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) } } -void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +void LLVoiceWebRTCSpatialConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) { LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); } -void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binary) +void LLVoiceWebRTCSpatialConnection::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 @@ -2514,7 +2543,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } } -void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) +void LLVoiceWebRTCSpatialConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { if (data_interface) { @@ -2535,7 +2564,7 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface } } -void LLVoiceWebRTCConnection::OnRenegotiationNeeded() +void LLVoiceWebRTCSpatialConnection::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; if (!mShutDown) @@ -2544,13 +2573,13 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() } } -void LLVoiceWebRTCConnection::OnPeerShutDown() +void LLVoiceWebRTCSpatialConnection::OnPeerShutDown() { setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); mOutstandingRequests--; } -void LLVoiceWebRTCConnection::processIceUpdates() +void LLVoiceWebRTCSpatialConnection::processIceUpdates() { if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) { @@ -2611,8 +2640,8 @@ void LLVoiceWebRTCConnection::processIceUpdates() url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), - boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateComplete, this, iceCompleted, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); mOutstandingRequests++; mTrickling = true; } @@ -2620,7 +2649,7 @@ void LLVoiceWebRTCConnection::processIceUpdates() } } -bool LLVoiceWebRTCConnection::requestVoiceConnection() +bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() { LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); @@ -2657,13 +2686,13 @@ bool LLVoiceWebRTCConnection::requestVoiceConnection() url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); mOutstandingRequests++; return true; } -void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2690,7 +2719,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result mOutstandingRequests--; } -void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2702,8 +2731,8 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); return; } LL_WARNS("Voice") << "Unable to connect voice." << body << " RESULT: " << result << LL_ENDL; @@ -2711,7 +2740,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i mOutstandingRequests--; } -bool LLVoiceWebRTCConnection::connectionStateMachine() +bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() { processIceUpdates(); @@ -2824,7 +2853,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } -void LLVoiceWebRTCConnection::sendData(const std::string& data) { +void LLVoiceWebRTCSpatialConnection::sendData(const std::string& data) { if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) { @@ -2832,7 +2861,7 @@ void LLVoiceWebRTCConnection::sendData(const std::string& data) { } } -bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) +bool LLVoiceWebRTCSpatialConnection::breakVoiceConnection(bool corowait) { LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; if (mWebRTCDataInterface) @@ -2870,14 +2899,14 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + 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) +void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2896,7 +2925,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res mOutstandingRequests--; } -void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2908,8 +2937,8 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); return; } if (mWebRTCPeerConnection) @@ -2924,7 +2953,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url mOutstandingRequests--; } -void LLVoiceWebRTCConnection::setMuteMic(bool muted) +void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) { mMuted = muted; if (mWebRTCAudioInterface) @@ -2941,21 +2970,3 @@ void LLVoiceWebRTCConnection::setMuteMic(bool 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); - } -} diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index bf5c68ed2b..ce4ab9fed6 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -144,19 +144,23 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string &uri, - const std::string &credentials) override; + void setNonSpatialChannel(const std::string& uri, + const std::string& credentials) override + { + + } bool setSpatialChannel(const std::string &uri, const std::string &credentials) override { - return setSpatialChannel(uri, credentials, INVALID_PARCEL_ID); + // this is a vivox-related call + return false; } - - bool setSpatialChannel(const std::string &uri, const std::string &credentials, S32 localParcelID); void leaveNonSpatialChannel() override; - void leaveChannel(void) override; + void leaveChannel(void) override { leaveChannel(true); } + + void leaveChannel(bool stopTalking); // 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. @@ -305,22 +309,16 @@ public: typedef std::map participantMap; typedef std::map participantUUIDMap; - struct sessionState + class sessionState { public: typedef boost::shared_ptr ptr_t; typedef boost::weak_ptr wptr_t; typedef boost::function sessionFunc_t; - typedef enum e_session_type - { - SESSION_TYPE_ESTATE = 1, - SESSION_TYPE_PARCEL, - SESSION_TYPE_P2P - } ESessionType; - - static ptr_t createSession(const std::string& channelID, S32 parcel_local_id); - ~sessionState(); + + 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); @@ -338,7 +336,7 @@ public: static void processSessionStates(); - bool processConnectionStates(); + virtual bool processConnectionStates(); void sendData(const std::string &data); @@ -352,9 +350,8 @@ public: bool isEmpty() { return mWebRTCConnections.empty(); } - bool isSpatial() { return mSessionType == SESSION_TYPE_ESTATE || mSessionType == SESSION_TYPE_PARCEL; } - - ESessionType getSessionType() { return mSessionType; } + virtual bool isSpatial() = 0; + virtual bool isEstate() = 0; std::string mHandle; std::string mGroupHandle; @@ -371,7 +368,6 @@ public: LLUUID mIMSessionID; LLUUID mCallerID; int mErrorStatusCode; - ESessionType mSessionType; bool mIncoming; bool mVoiceActive; @@ -389,27 +385,56 @@ public: LLUUID mVoiceFontID; - static void VerifySessions(); static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } + protected: + sessionState(); + std::list mWebRTCConnections; + private: - std::list mWebRTCConnections; static std::map mSessions; // canonical list of outstanding sessions. - sessionState(); - - static void for_eachPredicate(const std::pair &a, sessionFunc_t func); static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); }; - typedef boost::shared_ptr sessionStatePtr_t; + typedef boost::shared_ptr sessionStatePtr_t; typedef std::map sessionMap; + + class estateSessionState : public sessionState + { + public: + estateSessionState(); + virtual bool processConnectionStates() override; + + virtual bool isSpatial() { return true; } + virtual bool isEstate() { return true; } + }; + + class parcelSessionState : public sessionState + { + public: + parcelSessionState(const std::string& channelID, S32 parcel_local_id); + + virtual bool isSpatial() { return true; } + virtual bool isEstate() { return false; } + }; + + class adhocSessionState : public sessionState + { + public: + adhocSessionState(const std::string &channelID); + + virtual bool isSpatial() { return false; } + virtual bool isEstate() { return false; } + }; + + /////////////////////////////////////////////////////// // Private Member Functions @@ -479,11 +504,9 @@ public: void sessionEstablished(const LLUUID& region_id); sessionStatePtr_t findP2PSession(const LLUUID &agent_id); - sessionStatePtr_t addSession(const std::string& channel_id, S32 parcel_local_id); + sessionStatePtr_t addSession(const std::string &channel_id, sessionStatePtr_t &session); void deleteSession(const sessionStatePtr_t &session); - void verifySessionState(void); - // 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); @@ -578,10 +601,10 @@ private: std::string mChannelName; // Name of the channel to be looked up bool mAreaVoiceDisabled; - 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. + sessionStatePtr_t mSession; // Session state for the current session + bool mSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. - sessionStatePtr_t mNextAudioSession; // Session state for the audio session we're trying to join + sessionStatePtr_t mNextSession; // Session state for the session we're trying to join S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings std::string mCurrentRegionName; // Used to detect parcel boundary crossings @@ -598,10 +621,12 @@ private: bool mIsInitialized; bool mShutdownComplete; - - bool switchChannel(const std::string channelID, - sessionState::ESessionType sessionType, - S32 parcel_local_id = INVALID_PARCEL_ID); + + bool startEstateSession(); + bool startParcelSession(const std::string& channelID, S32 parcelID); + bool startAdHocSession(const std::string& channelID); + + void joinSession(const sessionStatePtr_t &session); std::string nameFromAvatar(LLVOAvatar *avatar); @@ -611,6 +636,9 @@ private: bool inSpatialChannel(); + bool inOrJoiningChannel(const std::string &channelID); + bool inEstateChannel(); + std::string getAudioSessionURI(); void setHidden(bool hidden) override; //virtual @@ -721,14 +749,42 @@ class LLVoiceWebRTCStats : public LLSingleton LLSD read(); }; -class LLVoiceWebRTCConnection : +class LLVoiceWebRTCConnection +{ + public: + LLVoiceWebRTCConnection(); + + virtual ~LLVoiceWebRTCConnection() = 0; + + virtual bool connectionStateMachine() = 0; + + virtual void sendData(const std::string &data) {}; + virtual void setMuteMic(bool muted); + virtual void setMicGain(F32 volume); + virtual void setSpeakerVolume(F32 volume); + + virtual void shutDown() = 0; + +protected: + + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; + + llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; +}; + + +class LLVoiceWebRTCSpatialConnection : + public LLVoiceWebRTCConnection, public llwebrtc::LLWebRTCSignalingObserver, public llwebrtc::LLWebRTCDataObserver { public: - LLVoiceWebRTCConnection(const LLUUID& regionID, S32 parcelLocalID, const std::string& channelID); + LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); - virtual ~LLVoiceWebRTCConnection(); + virtual ~LLVoiceWebRTCSpatialConnection(); ////////////////////////////// /// @name Signaling notification @@ -760,10 +816,8 @@ class LLVoiceWebRTCConnection : bool connectionStateMachine(); - void sendData(const std::string &data); - void setMuteMic(bool muted); - void setMicGain(F32 volume); - void setSpeakerVolume(F32 volume); + void sendData(const std::string &data) override; + void setMuteMic(bool muted) override; LLUUID getRegionID() { return mRegionID; } @@ -838,10 +892,6 @@ protected: bool mShutDown; S32 mOutstandingRequests; - bool mMuted; - F32 mMicGain; - F32 mSpeakerVolume; - std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- cgit v1.2.3 From e8edfbf3d488e1981a876a40714f36dafad5414a Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 23 Jan 2024 14:05:12 -0800 Subject: OSX build fixes --- indra/newview/llvoicewebrtc.cpp | 2 +- indra/newview/llvoicewebrtc.h | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0f42c8ea8a..c9180b7e99 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2062,7 +2062,7 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionStatePtr_t &session) +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionState::ptr_t session) { sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id); if (!existingSession) diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ce4ab9fed6..b2672ac108 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -410,10 +410,10 @@ public: { public: estateSessionState(); - virtual bool processConnectionStates() override; + bool processConnectionStates() override; - virtual bool isSpatial() { return true; } - virtual bool isEstate() { return true; } + bool isSpatial() override { return true; } + bool isEstate() override { return true; } }; class parcelSessionState : public sessionState @@ -421,8 +421,8 @@ public: public: parcelSessionState(const std::string& channelID, S32 parcel_local_id); - virtual bool isSpatial() { return true; } - virtual bool isEstate() { return false; } + bool isSpatial() override { return true; } + bool isEstate() override { return false; } }; class adhocSessionState : public sessionState @@ -430,8 +430,8 @@ public: public: adhocSessionState(const std::string &channelID); - virtual bool isSpatial() { return false; } - virtual bool isEstate() { return false; } + bool isSpatial() override { return false; } + bool isEstate() override { return false; } }; @@ -504,7 +504,7 @@ public: void sessionEstablished(const LLUUID& region_id); sessionStatePtr_t findP2PSession(const LLUUID &agent_id); - sessionStatePtr_t addSession(const std::string &channel_id, sessionStatePtr_t &session); + sessionStatePtr_t addSession(const std::string &channel_id, sessionState::ptr_t session); void deleteSession(const sessionStatePtr_t &session); // This is called in several places where the session _may_ need to be deleted. @@ -814,14 +814,14 @@ class LLVoiceWebRTCSpatialConnection : void OnVoiceConnectionRequestSuccess(const LLSD &body); void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); - bool connectionStateMachine(); + bool connectionStateMachine() override; void sendData(const std::string &data) override; void setMuteMic(bool muted) override; LLUUID getRegionID() { return mRegionID; } - void shutDown() + void shutDown() override { LLMutexLock lock(&mVoiceStateMutex); mShutDown = true; -- cgit v1.2.3 From bb2994d9ba89f199c08d88d89257f7944117a234 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 25 Jan 2024 20:53:19 -0800 Subject: Checkpoint Ad-Hoc voice. Unlike vivox, P2P uses the ad-hoc voice mechanism, which is also used by group voice. --- indra/newview/llimview.cpp | 8 +- indra/newview/llvoicechannel.cpp | 1 + indra/newview/llvoiceclient.cpp | 7 + indra/newview/llvoiceclient.h | 2 + indra/newview/llvoicewebrtc.cpp | 693 ++++++++++++++++++++++----------------- indra/newview/llvoicewebrtc.h | 229 +++++++------ 6 files changed, 513 insertions(+), 427 deletions(-) (limited to 'indra') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 61a01d7418..3e03dbef8f 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -665,8 +665,11 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& // set P2P type by default mSessionType = P2P_SESSION; - if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) + if ((IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) && LLVoiceClient::getInstance()->hasP2PInterface()) { + // 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); } else @@ -2034,7 +2037,8 @@ bool LLIMModel::sendStartSession( return true; } - else if ( dialog == IM_SESSION_CONFERENCE_START ) + else if (( dialog == IM_SESSION_CONFERENCE_START ) || + (((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) && !LLVoiceClient::getInstance()->hasP2PInterface())) { LLSD agents; for (int i = 0; i < (S32) ids.size(); i++) diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index b0eb8d962c..afac9ed6f8 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -654,6 +654,7 @@ void LLVoiceChannelGroup::voiceCallCapCoro(std::string url) LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " << iter->first << LL_ENDL; } + LL_INFOS("Voice") << "LLVoiceCallCapResponder::result got " << result << LL_ENDL; channelp->setChannelInfo( result["voice_credentials"]["channel_uri"].asString(), diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 294ae0c9ad..54840a1235 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -486,6 +486,13 @@ std::string LLVoiceClient::getCurrentChannel() //--------------------------------------- // invitations +bool LLVoiceClient::hasP2PInterface() +{ + if (mVoiceModule) + return mVoiceModule->hasP2PInterface(); + return false; +} + void LLVoiceClient::callUser(const LLUUID &uuid) { if (mVoiceModule) mVoiceModule->callUser(uuid); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 1e8ff21b4b..1a20de6109 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -186,6 +186,7 @@ public: /// @name invitations //@{ // start a voice channel with the specified user + virtual bool hasP2PInterface()=0; virtual void callUser(const LLUUID &uuid)=0; virtual bool isValidChannel(std::string& channelHandle)=0; virtual bool answerInvite(std::string &channelHandle)=0; @@ -382,6 +383,7 @@ public: // 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 + bool hasP2PInterface(); // true - can use the following. false - use conference/ad-hoc instead void callUser(const LLUUID &uuid); bool isValidChannel(std::string& channelHandle); bool answerInvite(std::string &channelHandle); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index c9180b7e99..fcdd818757 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -419,15 +419,27 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, } mSession = mNextSession; mNextSession.reset(); - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } - else if (mSession && mSession->mChannelID == channelID) + + if (mSession && mSession->mChannelID == channelID) { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } } } +void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID) +{ + if (gAgent.getRegion()->getRegionID() == regionID) + { + if (mSession && mSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + } + } +} + void LLWebRTCVoiceClient::idle(void* user_data) { } @@ -492,8 +504,12 @@ LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &c mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); } -LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID) +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, const std::string& credentials) : + mCredentials(credentials) { + LLUUID region_id = gAgent.getRegion()->getRegionID(); + mChannelID = channelID; + mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); } bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() @@ -1112,7 +1128,6 @@ bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) return false; } - LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipant(const std::string &uri) { participantStatePtr_t result; @@ -1192,10 +1207,10 @@ bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 p return true; } -bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID) +bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID, const std::string &credentials) { leaveChannel(false); - mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID))); + mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials))); return true; } @@ -1211,9 +1226,8 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) -{ - startAdHocSession(uuid.asString()); +void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) +{ } void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) @@ -1226,16 +1240,7 @@ bool LLWebRTCVoiceClient::isValidChannel(std::string &channelID) } bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) -{ - // this is only ever used to answer incoming p2p call invites. - - sessionStatePtr_t session(findP2PSession(LLUUID(channelID))); - if(session) - { - startAdHocSession(channelID); - return true; - } - +{ return false; } @@ -1419,7 +1424,11 @@ bool LLWebRTCVoiceClient::inSpatialChannel() { bool result = true; - if(mSession) + if (mNextSession) + { + result = mNextSession->isSpatial(); + } + else if(mSession) { result = mSession->isSpatial(); } @@ -2299,77 +2308,25 @@ std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asSt ///////////////////////////// // LLVoiceWebRTCConnection -LLVoiceWebRTCConnection::LLVoiceWebRTCConnection() : - mWebRTCAudioInterface(nullptr), - mWebRTCPeerConnection(nullptr), - mMuted(true), - mSpeakerVolume(0.0), - mMicGain(0.0) -{ - -} - -LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() -{ - if (LLWebRTCVoiceClient::isShuttingDown()) - { - // peer connection and observers will be cleaned up - // by llwebrtc::terminate() on shutdown. - return; - } - llwebrtc::freePeerConnection(mWebRTCPeerConnection); - mWebRTCPeerConnection = nullptr; -} - - -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); - } -} - -///////////////////////////// -// WebRTC Signaling Handlers - -LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID) : - mWebRTCPeerConnection(nullptr), +LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID) : mWebRTCAudioInterface(nullptr), mWebRTCDataInterface(nullptr), - mIceCompleted(false), - mTrickling(false), mVoiceConnectionState(VOICE_STATE_START_SESSION), - mChannelID(channelID), - mRegionID(regionID), - mParcelLocalID(parcelLocalID), + mMuted(true), mShutDown(false), - mOutstandingRequests(0) + mTrickling(false), + mIceCompleted(false), + mSpeakerVolume(0.0), + mMicGain(0.0), + mOutstandingRequests(0), + mChannelID(channelID), + mRegionID(regionID) { mWebRTCPeerConnection = llwebrtc::newPeerConnection(); mWebRTCPeerConnection->setSignalingObserver(this); } -LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() +LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2377,11 +2334,11 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() // by llwebrtc::terminate() on shutdown. return; } - assert(mOutstandingRequests == 0); - mWebRTCPeerConnection->unsetSignalingObserver(this); + llwebrtc::freePeerConnection(mWebRTCPeerConnection); + mWebRTCPeerConnection = nullptr; } -void LLVoiceWebRTCSpatialConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; @@ -2403,13 +2360,13 @@ void LLVoiceWebRTCSpatialConnection::OnIceGatheringState(llwebrtc::LLWebRTCSigna } } -void LLVoiceWebRTCSpatialConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { LLMutexLock lock(&mVoiceStateMutex); mIceCandidates.push_back(candidate); } -void LLVoiceWebRTCSpatialConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) +void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2419,7 +2376,7 @@ void LLVoiceWebRTCSpatialConnection::onIceUpdateComplete(bool ice_completed, con mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) +void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2435,8 +2392,8 @@ void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string u url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateComplete, this, ice_completed, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); + boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1), + boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); return; } @@ -2450,7 +2407,7 @@ void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string u mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::OnOfferAvailable(const std::string &sdp) +void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; LLMutexLock lock(&mVoiceStateMutex); @@ -2461,110 +2418,14 @@ void LLVoiceWebRTCSpatialConnection::OnOfferAvailable(const std::string &sdp) } } -void LLVoiceWebRTCSpatialConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) { LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); } -void LLVoiceWebRTCSpatialConnection::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' - join - object of join data (TBD) (true for now) - // 'l' - boolean, always true if exists. - - 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; - 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; - if (voice_data[participant_id].isMember("j")) - { - joined = true; - primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); - } - - new_participant |= joined; - if (!participant && joined && primary) - { - participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); - } - if (participant) - { - if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) - { - if (agent_id != gAgentID) - { - LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); - } - } - else - { - F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; - // convert to decibles - participant->mLevel = level; - /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we - can use it at some point when we actually process frames. */ - participant->mIsSpeaking = participant->mLevel > SPEAKING_AUDIO_LEVEL; - } - } - } - } -} - -void LLVoiceWebRTCSpatialConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) -{ - if (data_interface) - { - mWebRTCDataInterface = data_interface; - mWebRTCDataInterface->setDataObserver(this); - - Json::FastWriter writer; - Json::Value root = Json::objectValue; - Json::Value join_obj = Json::objectValue; - LLUUID regionID = gAgent.getRegion()->getRegionID(); - if (regionID == mRegionID) - { - join_obj["p"] = true; - } - root["j"] = join_obj; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); - } -} - -void LLVoiceWebRTCSpatialConnection::OnRenegotiationNeeded() +void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; if (!mShutDown) @@ -2573,13 +2434,13 @@ void LLVoiceWebRTCSpatialConnection::OnRenegotiationNeeded() } } -void LLVoiceWebRTCSpatialConnection::OnPeerShutDown() +void LLVoiceWebRTCConnection::OnPeerShutDown() { setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::processIceUpdates() +void LLVoiceWebRTCConnection::processIceUpdates() { if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) { @@ -2596,39 +2457,40 @@ void LLVoiceWebRTCSpatialConnection::processIceUpdates() LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { - LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; - return; + LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; + return; } std::string url = regionp->getCapability("VoiceSignalingRequest"); if (url.empty()) { - return; + 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) - { + if (!mIceCandidates.empty()) + { + LLSD candidates = LLSD::emptyArray(); + for (auto &ice_candidate : mIceCandidates) + { LLSD body_candidate; body_candidate["sdpMid"] = ice_candidate.sdp_mid; body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; body_candidate["candidate"] = ice_candidate.candidate; candidates.append(body_candidate); - } - body["candidates"] = candidates; - mIceCandidates.clear(); + } + body["candidates"] = candidates; + mIceCandidates.clear(); } else if (mIceCompleted) { - LLSD body_candidate; - body_candidate["completed"] = true; - body["candidate"] = body_candidate; - iceCompleted = mIceCompleted; - mIceCompleted = false; + LLSD body_candidate; + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + iceCompleted = mIceCompleted; + mIceCompleted = false; } - + body["viewer_session"] = mViewerSession; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -2649,14 +2511,56 @@ void LLVoiceWebRTCSpatialConnection::processIceUpdates() } } -bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() + +void LLVoiceWebRTCConnection::setMuteMic(bool muted) { - LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + mMuted = muted; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setMute(muted); + } +} - LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; +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::sendData(const std::string &data) +{ + if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) + { + mWebRTCDataInterface->sendData(data, false); + } +} + +bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) +{ + LL_INFOS("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; } @@ -2666,60 +2570,105 @@ bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() return false; } - LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; + 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; - 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["logout"] = TRUE; + body["viewer_session"] = mViewerSession; 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)); + 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 LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) { 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")) + if (mWebRTCPeerConnection) { - mRemoteChannelSDP = result["jsep"]["sdp"].asString(); - mViewerSession = result["viewer_session"]; + mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); } else { - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - mOutstandingRequests--; - return; + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } + mOutstandingRequests--; +} - LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" +void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + if (retries >= 0) + { + 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 (mWebRTCPeerConnection) + { + mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + } + mOutstandingRequests--; +} + + +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +{ + 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 + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + mOutstandingRequests--; + return; + } + + LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" << " channel sdp " << mRemoteChannelSDP << LL_ENDL; mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2731,17 +2680,17 @@ void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure(std::string url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + 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); + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; } -bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() -{ +bool LLVoiceWebRTCConnection::connectionStateMachine() +{ processIceUpdates(); switch (getVoiceConnectionState()) @@ -2819,7 +2768,6 @@ bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; - break; case VOICE_STATE_DISCONNECT: breakVoiceConnection(true); @@ -2831,13 +2779,17 @@ bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() { { LLMutexLock lock(&mVoiceStateMutex); - if (!mShutDown) + if (!mShutDown) { mVoiceConnectionState = VOICE_STATE_START_SESSION; } else { - return mOutstandingRequests > 0; + if (mOutstandingRequests <= 0) + { + LLWebRTCVoiceClient::getInstance()->OnConnectionShutDown(mChannelID, mRegionID); + return false; + } } } break; @@ -2852,29 +2804,133 @@ bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() return true; } +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' - join - object of join data (TBD) (true for now) + // 'l' - boolean, always true if exists. -void LLVoiceWebRTCSpatialConnection::sendData(const std::string& data) { - - if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) + if (binary) { - mWebRTCDataInterface->sendData(data, false); + 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; + 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; + if (voice_data[participant_id].isMember("j")) + { + joined = true; + primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + } + + new_participant |= joined; + if (!participant && joined && primary) + { + participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); + } + if (participant) + { + if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) + { + if (agent_id != gAgentID) + { + LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); + } + } + else + { + F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; + // convert to decibles + participant->mLevel = level; + /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we + can use it at some point when we actually process frames. */ + participant->mIsSpeaking = participant->mLevel > SPEAKING_AUDIO_LEVEL; + } + } + } } } -bool LLVoiceWebRTCSpatialConnection::breakVoiceConnection(bool corowait) +void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { - LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; - if (mWebRTCDataInterface) + if (data_interface) { - mWebRTCDataInterface->unsetDataObserver(this); - mWebRTCDataInterface = nullptr; + mWebRTCDataInterface = data_interface; + mWebRTCDataInterface->setDataObserver(this); + + Json::FastWriter writer; + Json::Value root = Json::objectValue; + Json::Value join_obj = Json::objectValue; + LLUUID regionID = gAgent.getRegion()->getRegionID(); + if (regionID == mRegionID) + { + join_obj["p"] = true; + } + root["j"] = join_obj; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); } - mWebRTCAudioInterface = nullptr; +} + +///////////////////////////// +// WebRTC Spatial Connection + +LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, 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); + mWebRTCPeerConnection->unsetSignalingObserver(this); +} + + +bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() +{ LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + + LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); return false; } @@ -2884,89 +2940,110 @@ bool LLVoiceWebRTCSpatialConnection::breakVoiceConnection(bool corowait) 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); + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); LLSD body; - body["logout"] = TRUE; - body["viewer_session"] = mViewerSession; + LLSD jsep; + jsep["type"] = "offer"; + { + LLMutexLock lock(&mVoiceStateMutex); + jsep["sdp"] = mChannelSDP; + } + body["jsep"] = jsep; + if (mParcelLocalID != INVALID_PARCEL_ID) + { + body["parcel_local_id"] = mParcelLocalID; + } 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); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); mOutstandingRequests++; return true; } -void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) +void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) { - if (LLWebRTCVoiceClient::isShuttingDown()) + mMuted = muted; + if (mWebRTCAudioInterface) { - return; + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp && mRegionID == regionp->getRegionID()) + { + mWebRTCAudioInterface->setMute(muted); + } + else + { + // always mute to regions the agent isn't on, to prevent echo. + mWebRTCAudioInterface->setMute(true); + } } +} - if (mWebRTCPeerConnection) - { - mOutstandingRequests++; - mWebRTCPeerConnection->shutdownConnection(); - } - else - { - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); - } - mOutstandingRequests--; +///////////////////////////// +// WebRTC Spatial Connection + +LLVoiceWebRTCAdHocConnection::LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, const std::string& channelID, const std::string& credentials) : + LLVoiceWebRTCConnection(regionID, channelID), + mCredentials(credentials) +{ } -void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection() { if (LLWebRTCVoiceClient::isShuttingDown()) { + // peer connection and observers will be cleaned up + // by llwebrtc::terminate() on shutdown. return; } - if (retries >= 0) - { - 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 (mWebRTCPeerConnection) + assert(mOutstandingRequests == 0); + mWebRTCPeerConnection->unsetSignalingObserver(this); +} + + +bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection() +{ + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + + LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; + if (!regionp || !regionp->capabilitiesReceived()) { - mOutstandingRequests++; - mWebRTCPeerConnection->shutdownConnection(); + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + return false; } - else + + std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); + if (url.empty()) { - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + return false; } - mOutstandingRequests--; -} -void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) -{ - mMuted = muted; - if (mWebRTCAudioInterface) + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; + + LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); + LLSD body; + LLSD jsep; + jsep["type"] = "offer"; { - LLViewerRegion *regionp = gAgent.getRegion(); - if (regionp && mRegionID == regionp->getRegionID()) - { - mWebRTCAudioInterface->setMute(muted); - } - else - { - // always mute to regions the agent isn't on, to prevent echo. - mWebRTCAudioInterface->setMute(true); - } + LLMutexLock lock(&mVoiceStateMutex); + jsep["sdp"] = mChannelSDP; } + body["jsep"] = jsep; + body["credentials"] = mCredentials; + body["channel"] = mChannelID; + body["channel_type"] = "multiagent"; + + 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 index b2672ac108..f81c8c556e 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -144,10 +144,8 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string& uri, - const std::string& credentials) override - { - + void setNonSpatialChannel(const std::string& uri, const std::string& credentials) override { + startAdHocSession(uri, credentials); } bool setSpatialChannel(const std::string &uri, const std::string &credentials) override @@ -172,6 +170,7 @@ public: /// @name invitations //@{ // start a voice channel with the specified user + bool hasP2PInterface() override { return false; } void callUser(const LLUUID &uuid) override; bool isValidChannel(std::string &channelID) override; bool answerInvite(std::string &channelID) override; @@ -247,6 +246,7 @@ 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 sendPositionUpdate(bool force); void updateOwnVolume(); @@ -338,7 +338,7 @@ public: virtual bool processConnectionStates(); - void sendData(const std::string &data); + virtual void sendData(const std::string &data); void setMuteMic(bool muted); void setMicGain(F32 volume); @@ -427,11 +427,17 @@ public: class adhocSessionState : public sessionState { - public: - adhocSessionState(const std::string &channelID); + public: + adhocSessionState(const std::string &channelID, const std::string& credentials); bool isSpatial() override { return false; } bool isEstate() override { return false; } + + // don't send spatial data to adhoc sessions. + void sendData(const std::string &data) override { } + + protected: + std::string mCredentials; }; @@ -511,26 +517,6 @@ public: // It contains logic for whether to delete the session or keep it around. void reapSession(const sessionStatePtr_t &session); - ////////////////////////////////////// - // buddy list stuff, needed for SLIM later - struct buddyListEntry - { - buddyListEntry(const std::string &uri); - std::string mURI; - std::string mDisplayName; - LLUUID mUUID; - bool mOnlineSL; - bool mOnlineSLim; - bool mCanSeeMeOnline; - bool mHasBlockListEntry; - bool mHasAutoAcceptListEntry; - bool mNameResolved; - bool mInSLFriends; - bool mInWebRTCBuddies; - }; - - typedef std::map buddyListMap; - // Pokes the state machine to leave the audio session next time around. void sessionTerminate(); @@ -612,7 +598,6 @@ private: bool mBuddyListMapPopulated; bool mBlockRulesListReceived; bool mAutoAcceptRulesListReceived; - buddyListMap mBuddyListMap; llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; @@ -624,8 +609,7 @@ private: bool startEstateSession(); bool startParcelSession(const std::string& channelID, S32 parcelID); - bool startAdHocSession(const std::string& channelID); - + bool startAdHocSession(const std::string& channelID, const std::string& credentials); void joinSession(const sessionStatePtr_t &session); @@ -749,147 +733,126 @@ class LLVoiceWebRTCStats : public LLSingleton LLSD read(); }; -class LLVoiceWebRTCConnection -{ - public: - LLVoiceWebRTCConnection(); - - virtual ~LLVoiceWebRTCConnection() = 0; - - virtual bool connectionStateMachine() = 0; - - virtual void sendData(const std::string &data) {}; - virtual void setMuteMic(bool muted); - virtual void setMicGain(F32 volume); - virtual void setSpeakerVolume(F32 volume); - - virtual void shutDown() = 0; - -protected: - - bool mMuted; - F32 mMicGain; - F32 mSpeakerVolume; - - llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; - llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; -}; - - -class LLVoiceWebRTCSpatialConnection : - public LLVoiceWebRTCConnection, - public llwebrtc::LLWebRTCSignalingObserver, +class LLVoiceWebRTCConnection : + public llwebrtc::LLWebRTCSignalingObserver, public llwebrtc::LLWebRTCDataObserver { public: - LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); + LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID); - virtual ~LLVoiceWebRTCSpatialConnection(); + virtual ~LLVoiceWebRTCConnection() = 0; ////////////////////////////// /// @name Signaling notification // LLWebRTCSignalingObserver //@{ - void OnIceGatheringState(IceGatheringState 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 OnPeerShutDown() override; + virtual void OnIceGatheringState(IceGatheringState state); + virtual void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate); + virtual void OnOfferAvailable(const std::string &sdp); + virtual void OnRenegotiationNeeded() override; + virtual void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface); + virtual void OnPeerShutDown(); //@} ///////////////////////// /// @name Data Notification /// LLWebRTCDataObserver //@{ - void OnDataReceived(const std::string &data, bool binary) override; - void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; + virtual void OnDataReceived(const std::string &data, bool binary); + virtual void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface); //@} - void processIceUpdates(); - void onIceUpdateComplete(bool ice_completed, const LLSD &result); - void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); + void sendData(const std::string &data); - bool requestVoiceConnection(); - void OnVoiceConnectionRequestSuccess(const LLSD &body); - void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + 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); - bool connectionStateMachine() override; + virtual void setMuteMic(bool muted); + virtual void setMicGain(F32 volume); + virtual void setSpeakerVolume(F32 volume); - void sendData(const std::string &data) override; - void setMuteMic(bool muted) override; + + bool connectionStateMachine(); LLUUID getRegionID() { return mRegionID; } - void shutDown() override - { - LLMutexLock lock(&mVoiceStateMutex); - mShutDown = true; - } + void shutDown() + { + LLMutexLock lock(&mVoiceStateMutex); + mShutDown = true; + } -protected: + 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_SESSION_UP = 0x20, - VOICE_STATE_SESSION_RETRY = 0x40, - VOICE_STATE_DISCONNECT = 0x80, - VOICE_STATE_WAIT_FOR_EXIT = 0x100, - VOICE_STATE_SESSION_EXIT = 0x200, - VOICE_STATE_SESSION_STOPPING = 0x3c0, - VOICE_STATE_SESSION_WAITING = 0x10a + 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_SESSION_UP = 0x20, + VOICE_STATE_SESSION_RETRY = 0x40, + VOICE_STATE_DISCONNECT = 0x80, + VOICE_STATE_WAIT_FOR_EXIT = 0x100, + VOICE_STATE_SESSION_EXIT = 0x200, + VOICE_STATE_SESSION_STOPPING = 0x3c0, + VOICE_STATE_SESSION_WAITING = 0x10a } EVoiceConnectionState; EVoiceConnectionState mVoiceConnectionState; - LLMutex mVoiceStateMutex; - void setVoiceConnectionState(EVoiceConnectionState new_voice_connection_state) + 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. + 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. + { + // we're currently shutting down or restarting, so ignore any + // state changes. return; - } + } mVoiceConnectionState = new_voice_connection_state; } EVoiceConnectionState getVoiceConnectionState() { - if (mVoiceStateMutex.isLocked()) - { + 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; - LLUUID mViewerSession; - - std::string mChannelID; - LLUUID mRegionID; - S32 mParcelLocalID; + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; - bool mShutDown; + bool mShutDown; S32 mOutstandingRequests; std::vector mIceCandidates; @@ -901,6 +864,38 @@ protected: llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; }; + +class LLVoiceWebRTCSpatialConnection : + public LLVoiceWebRTCConnection +{ + public: + LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); + + virtual ~LLVoiceWebRTCSpatialConnection(); + + void setMuteMic(bool muted) override; + + +protected: + + bool requestVoiceConnection() override; + + S32 mParcelLocalID; +}; + +class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection +{ + public: + LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, const std::string &channelID, const std::string& credentials); + + virtual ~LLVoiceWebRTCAdHocConnection(); + + protected: + bool requestVoiceConnection() override; + + std::string mCredentials; +}; + #define VOICE_ELAPSED LLVoiceTimer(__FUNCTION__); #endif //LL_WebRTC_VOICE_CLIENT_H -- cgit v1.2.3 From 8e61bc80c076d90359e55274684036e437ddb9fa Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 29 Jan 2024 21:25:13 -0800 Subject: Treat adhoc/p2p as primary connections --- indra/newview/llvoicevivox.h | 13 +++++++------ indra/newview/llvoicewebrtc.cpp | 20 ++++++++++---------- indra/newview/llvoicewebrtc.h | 9 ++++++++- 3 files changed, 25 insertions(+), 17 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index e3ab99c675..bd1f18aec6 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -154,17 +154,18 @@ public: /// @name invitations //@{ // start a voice channel with the specified user - virtual void callUser(const LLUUID &uuid); - virtual bool isValidChannel(std::string &channelHandle); - virtual bool answerInvite(std::string &channelHandle); - virtual void declineInvite(std::string &channelHandle); + bool hasP2PInterface() override { return true; } + void callUser(const LLUUID &uuid); + bool isValidChannel(std::string &channelHandle) override; + bool answerInvite(std::string &channelHandle) override; + void declineInvite(std::string &channelHandle) override; //@} ///////////////////////// /// @name Volume/gain //@{ - virtual void setVoiceVolume(F32 volume); - virtual void setMicGain(F32 volume); + void setVoiceVolume(F32 volume) override; + void setMicGain(F32 volume) override; //@} ///////////////////////// diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index fcdd818757..a5c647c675 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2853,7 +2853,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } new_participant |= joined; - if (!participant && joined && primary) + if (!participant && joined && (primary || !isSpatial())) { participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); } @@ -2861,19 +2861,19 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) { - if (agent_id != gAgentID) - { + if (agent_id != gAgentID) + { LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); - } + } } else { - F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; - // convert to decibles - participant->mLevel = level; - /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we - can use it at some point when we actually process frames. */ - participant->mIsSpeaking = participant->mLevel > SPEAKING_AUDIO_LEVEL; + F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; + // convert to decibles + participant->mLevel = level; + /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we + can use it at some point when we actually process frames. */ + participant->mIsSpeaking = participant->mLevel > SPEAKING_AUDIO_LEVEL; } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f81c8c556e..38d867db8d 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -150,8 +150,9 @@ public: bool setSpatialChannel(const std::string &uri, const std::string &credentials) override { + leaveNonSpatialChannel(); // this is a vivox-related call - return false; + return true; } void leaveNonSpatialChannel() override; @@ -775,6 +776,8 @@ class LLVoiceWebRTCConnection : bool connectionStateMachine(); + virtual bool isSpatial() = 0; + LLUUID getRegionID() { return mRegionID; } void shutDown() @@ -875,6 +878,8 @@ class LLVoiceWebRTCSpatialConnection : void setMuteMic(bool muted) override; + bool isSpatial() override { return true; } + protected: @@ -890,6 +895,8 @@ class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection virtual ~LLVoiceWebRTCAdHocConnection(); + bool isSpatial() override { return false; } + protected: bool requestVoiceConnection() override; -- cgit v1.2.3 From d3ab894c879bbd5c5e4fc7ccd61b8c0e78d7dc67 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 29 Jan 2024 21:26:36 -0800 Subject: mac build fixes --- indra/newview/llvoicewebrtc.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 38d867db8d..6562a737ae 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -747,20 +747,20 @@ class LLVoiceWebRTCConnection : /// @name Signaling notification // LLWebRTCSignalingObserver //@{ - virtual void OnIceGatheringState(IceGatheringState state); - virtual void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate); - virtual void OnOfferAvailable(const std::string &sdp); - virtual void OnRenegotiationNeeded() override; - virtual void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface); - virtual void OnPeerShutDown(); + void OnIceGatheringState(IceGatheringState 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 OnPeerShutDown() override; //@} ///////////////////////// /// @name Data Notification /// LLWebRTCDataObserver //@{ - virtual void OnDataReceived(const std::string &data, bool binary); - virtual void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface); + void OnDataReceived(const std::string &data, bool binary) override; + void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; //@} void sendData(const std::string &data); -- cgit v1.2.3 From bc05e7dcf4d64873816a96f04d6772c2a75be37c Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 1 Feb 2024 09:20:11 -0800 Subject: checkpoint p2p/adhoc voice --- indra/newview/llimview.cpp | 7 +++++- indra/newview/llvoicewebrtc.cpp | 51 ++++++++++++++++++----------------------- indra/newview/llvoicewebrtc.h | 11 +-------- 3 files changed, 29 insertions(+), 40 deletions(-) (limited to 'indra') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 3e03dbef8f..78017337e8 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -681,10 +681,15 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& { mSessionType = GROUP_SESSION; } - else + else if (LLVoiceClient::getInstance()->hasP2PInterface()) { mSessionType = ADHOC_SESSION; } + else + { + // webrtc uses adhoc channels for p2p + mSessionType = P2P_SESSION; + } } if(mVoiceChannel) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a5c647c675..b10e967986 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -235,7 +235,6 @@ bool LLWebRTCVoiceClient::sConnected = false; LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr; LLWebRTCVoiceClient::LLWebRTCVoiceClient() : - mSessionTerminateRequested(false), mRelogRequested(false), mSpatialJoiningNum(0), @@ -247,7 +246,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mAreaVoiceDisabled(false), mSession(), // TBD - should be NULL - mSessionChanged(false), mNextSession(), mCurrentParcelLocalID(0), @@ -419,12 +417,13 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, } mSession = mNextSession; mNextSession.reset(); + mSession->addParticipant(gAgentID); } if (mSession && mSession->mChannelID == channelID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); } } } @@ -435,7 +434,7 @@ void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, con { if (mSession && mSession->mChannelID == channelID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + //LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); } } } @@ -651,29 +650,17 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() //========================================================================= -void LLWebRTCVoiceClient::sessionTerminate() -{ - mSessionTerminateRequested = true; -} - -void LLWebRTCVoiceClient::requestRelog() -{ - mSessionTerminateRequested = true; - mRelogRequested = true; -} - - 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; } - sessionTerminate(); } void LLWebRTCVoiceClient::clearCaptureDevices() @@ -1018,7 +1005,6 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad result.reset(new participantState(agent_id)); mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result)); - mParticipantsChanged = true; result->mAvatarIDValid = true; result->mAvatarID = agent_id; @@ -1033,6 +1019,10 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad result->mVolumeDirty = true; mVolumeDirty = true; } + if (!LLWebRTCVoiceClient::sShuttingDown) + { + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); + } LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; } @@ -1084,8 +1074,10 @@ void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceCli { mParticipantsByURI.erase(iter); mParticipantsByUUID.erase(iter2); - - mParticipantsChanged = true; + if (!LLWebRTCVoiceClient::sShuttingDown) + { + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); + } } } } @@ -1222,7 +1214,7 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. - sessionTerminate(); + mSession->shutdownAllConnections(); } } @@ -1308,7 +1300,7 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel() // Most likely this will still be the current session at this point, but check it anyway. reapSession(oldNextSession); - sessionTerminate(); + leaveChannel(true); } std::string LLWebRTCVoiceClient::getCurrentChannel() @@ -1612,9 +1604,12 @@ void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. - sessionTerminate(); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + bool wasShuttingDown = mSession->mShuttingDown; deleteSession(mSession); + if (!wasShuttingDown) + { + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + } } if (mNextSession) @@ -1949,7 +1944,6 @@ LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), mVolumeDirty(false), mMuteDirty(false), - mParticipantsChanged(false), mShuttingDown(false) { } @@ -1959,7 +1953,6 @@ void LLWebRTCVoiceClient::sessionState::addSession( const std::string &channelID, LLWebRTCVoiceClient::sessionState::ptr_t& session) { - session->addParticipant(gAgentID); mSessions[channelID] = session; } @@ -2114,7 +2107,6 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) if (deleteAudioSession) { mSession.reset(); - mSessionChanged = true; } // ditto for the next audio session @@ -2210,7 +2202,8 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt << ", proximal is " << inSpatialChannel() << LL_ENDL; - mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN; + mIsProcessingChannels = + (status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN || status == LLVoiceClientStatusObserver::STATUS_JOINED); for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end(); @@ -2285,7 +2278,7 @@ void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::se { // Found -- fill in the name // and post a "participants updated" message to listeners later. - session->mParticipantsChanged = true; + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); } // Check whether this is a p2p session whose caller name just resolved diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 6562a737ae..5ddfbd9ea4 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -380,7 +380,6 @@ public: bool mVolumeDirty; bool mMuteDirty; - bool mParticipantsChanged; participantMap mParticipantsByURI; participantUUIDMap mParticipantsByUUID; @@ -516,13 +515,7 @@ public: // 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); - - // Pokes the state machine to leave the audio session next time around. - void sessionTerminate(); - - // Pokes the state machine to shut down the connector and restart it. - void requestRelog(); + void reapSession(const sessionStatePtr_t &session); // Does the actual work to get out of the audio session void leaveAudioSession(); @@ -567,7 +560,6 @@ private: /// Clean up objects created during a voice session. void cleanUp(); - bool mSessionTerminateRequested; bool mRelogRequested; // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). // The larger it is the greater is possibility there is a problem with connection to voice server. @@ -589,7 +581,6 @@ private: std::string mChannelName; // Name of the channel to be looked up bool mAreaVoiceDisabled; sessionStatePtr_t mSession; // Session state for the current session - bool mSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. sessionStatePtr_t mNextSession; // Session state for the session we're trying to join -- cgit v1.2.3 From ead4feb6f56a2d62cdb3dcccbca683f340347f92 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 1 Feb 2024 21:53:33 -0800 Subject: Hang up when peer hangs up in ad-hoc driven p2p call --- indra/newview/llvoicechannel.cpp | 30 ++++++++++++++++++++++++++++-- indra/newview/llvoiceclient.cpp | 5 +++-- indra/newview/llvoiceclient.h | 10 +++++++--- indra/newview/llvoicevivox.cpp | 3 ++- indra/newview/llvoicevivox.h | 3 ++- indra/newview/llvoicewebrtc.cpp | 29 +++++++++++++++-------------- indra/newview/llvoicewebrtc.h | 10 ++++++---- 7 files changed, 63 insertions(+), 27 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index afac9ed6f8..05c22dd645 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -437,7 +437,8 @@ void LLVoiceChannelGroup::activate() // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, - mCredentials); + mCredentials, + !LLVoiceClient::getInstance()->hasP2PInterface()); if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel { @@ -518,7 +519,8 @@ void LLVoiceChannelGroup::setChannelInfo( // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, - mCredentials); + mCredentials, + !LLVoiceClient::getInstance()->hasP2PInterface()); } } @@ -527,6 +529,30 @@ void LLVoiceChannelGroup::handleStatusChange(EStatusType type) // status updates switch(type) { + case STATUS_LEFT_CHANNEL: + { + if (!LLVoiceClient::getInstance()->hasP2PInterface()) + { + // we're using group/adhoc for p2p + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) + { + // *TODO: use it to show DECLINE voice notification + if (mState == STATE_RINGING) + { + // other user declined call + LLNotificationsUtil::add("P2PCallDeclined", mNotifyArgs); + } + else + { + // other user hung up, so we didn't end the call + mCallEndedByAgent = false; + } + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + return; + } + } case STATUS_JOINED: mRetries = 3; mIsRetrying = FALSE; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 54840a1235..de7fb248b0 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -436,11 +436,12 @@ bool LLVoiceClient::inProximalChannel() void LLVoiceClient::setNonSpatialChannel( const std::string &uri, - const std::string &credentials) + const std::string &credentials, + bool hangup_on_last_leave) { if (mVoiceModule) { - mVoiceModule->setNonSpatialChannel(uri, credentials); + mVoiceModule->setNonSpatialChannel(uri, credentials, hangup_on_last_leave); } } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 1a20de6109..69004f2000 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -167,7 +167,8 @@ public: virtual bool inProximalChannel()=0; virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials)=0; + const std::string &credentials, + bool hangup_on_last_leave = false)=0; virtual bool setSpatialChannel(const std::string &uri, const std::string &credentials)=0; @@ -371,9 +372,12 @@ public: // 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); + const std::string &uri, + const std::string &credentials, + bool hangup_on_last_leave = false); + void setSpatialChannel( const std::string &uri, const std::string &credentials); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 3725510b6a..3c01f00596 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -5025,7 +5025,8 @@ void LLVivoxVoiceClient::joinSession(const sessionStatePtr_t &session) void LLVivoxVoiceClient::setNonSpatialChannel( const std::string &uri, - const std::string &credentials) + const std::string &credentials, + bool hangup_on_last_leave) { switchChannel(uri, false, false, false, credentials); } diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index bd1f18aec6..929ccce32b 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -135,7 +135,8 @@ public: virtual bool inProximalChannel(); virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials); + const std::string &credentials, + bool hangup_on_last_leave); virtual bool setSpatialChannel(const std::string &uri, const std::string &credentials); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b10e967986..17b61465cc 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -485,11 +485,9 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() } -////////////////////////// -// LLWebRTCVoiceClient::estateSessionState - LLWebRTCVoiceClient::estateSessionState::estateSessionState() { + mHangupOnLastLeave = false; mChannelID = "Estate"; LLUUID region_id = gAgent.getRegion()->getRegionID(); @@ -498,14 +496,16 @@ LLWebRTCVoiceClient::estateSessionState::estateSessionState() LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id) { + mHangupOnLastLeave = 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) : +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, const std::string& credentials, bool hangup_on_last_leave) : mCredentials(credentials) { + mHangupOnLastLeave = hangup_on_last_leave; LLUUID region_id = gAgent.getRegion()->getRegionID(); mChannelID = channelID; mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); @@ -1181,6 +1181,12 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co if (participant) { session->removeParticipant(participant); + if (session->mHangupOnLastLeave && + (id != gAgentID) && + (session->mParticipantsByURI.size() <= 1)) + { + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + } } } } @@ -1199,10 +1205,10 @@ bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 p return true; } -bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID, const std::string &credentials) +bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID, const std::string &credentials, bool hangup_on_last_leave) { leaveChannel(false); - mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials))); + mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials, hangup_on_last_leave))); return true; } @@ -1602,14 +1608,7 @@ void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) if (mSession) { - // If we're already in a channel, or if we're joining one, terminate - // so we can rejoin with the new session data. - bool wasShuttingDown = mSession->mShuttingDown; deleteSession(mSession); - if (!wasShuttingDown) - { - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - } } if (mNextSession) @@ -2980,7 +2979,9 @@ void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) ///////////////////////////// // WebRTC Spatial Connection -LLVoiceWebRTCAdHocConnection::LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, const std::string& channelID, const std::string& credentials) : +LLVoiceWebRTCAdHocConnection::LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, + const std::string& channelID, + const std::string& credentials) : LLVoiceWebRTCConnection(regionID, channelID), mCredentials(credentials) { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 5ddfbd9ea4..e1a740929b 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -144,8 +144,8 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string& uri, const std::string& credentials) override { - startAdHocSession(uri, credentials); + void setNonSpatialChannel(const std::string& uri, const std::string& credentials, bool hangup_on_last_leave) override { + startAdHocSession(uri, credentials, hangup_on_last_leave); } bool setSpatialChannel(const std::string &uri, const std::string &credentials) override @@ -388,6 +388,8 @@ public: static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } + bool mHangupOnLastLeave; + protected: sessionState(); std::list mWebRTCConnections; @@ -428,7 +430,7 @@ public: class adhocSessionState : public sessionState { public: - adhocSessionState(const std::string &channelID, const std::string& credentials); + adhocSessionState(const std::string &channelID, const std::string& credentials, bool hangup_on_last_leave); bool isSpatial() override { return false; } bool isEstate() override { return false; } @@ -601,7 +603,7 @@ private: bool startEstateSession(); bool startParcelSession(const std::string& channelID, S32 parcelID); - bool startAdHocSession(const std::string& channelID, const std::string& credentials); + bool startAdHocSession(const std::string& channelID, const std::string& credentials, bool hangup_on_last_leave); void joinSession(const sessionStatePtr_t &session); -- cgit v1.2.3 From 2817cc55a92f84aef81fb9cc378046b3dec7b4be Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 1 Feb 2024 21:57:05 -0800 Subject: fix mac build break --- indra/newview/llvoicevivox.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 929ccce32b..6e6ffdb1e4 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -134,9 +134,9 @@ public: // Note that gestures should only fire if this returns true. virtual bool inProximalChannel(); - virtual void setNonSpatialChannel(const std::string &uri, + void setNonSpatialChannel(const std::string &uri, const std::string &credentials, - bool hangup_on_last_leave); + bool hangup_on_last_leave) override; virtual bool setSpatialChannel(const std::string &uri, const std::string &credentials); -- cgit v1.2.3 From da7dfd8186d378b4d87f0bdecad29cf0f51e0793 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 3 Feb 2024 18:29:17 -0800 Subject: Checkpoint mute/volume --- indra/newview/llvoicewebrtc.cpp | 115 ++++++++++++++++++++++++++++++++++++---- indra/newview/llvoicewebrtc.h | 20 ++++++- 2 files changed, 124 insertions(+), 11 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 17b61465cc..e59e267d31 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -291,10 +291,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.serverType = VOICE_SERVER_TYPE; - // 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 WebRTC daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. @@ -550,6 +546,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() LLCoros::set_consuming(true); try { + LLMuteList::getInstance()->addObserver(this); while (!sShuttingDown) { llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); @@ -919,6 +916,26 @@ void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) } } +void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID& id, F32 volume) +{ + 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); + } +} + void LLWebRTCVoiceClient::sendLocalAudioUpdates() { } @@ -1650,6 +1667,20 @@ void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionState session->setMicGain(gain); } +void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, + const LLUUID &id, + bool mute) +{ + session->setUserMute(id, mute); +} + +void LLWebRTCVoiceClient::predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, + const LLUUID &id, + F32 volume) +{ + session->setUserVolume(id, volume); +} + void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) { LL_DEBUGS("Voice") @@ -1793,8 +1824,6 @@ std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) return result; } - - BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; @@ -1893,6 +1922,7 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) 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->findParticipant(id.asString())); @@ -1910,11 +1940,12 @@ void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); } - participant->mVolume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); + participant->mVolume = clamped_volume; participant->mVolumeDirty = true; mSession->mVolumeDirty = true; } } + sessionState::for_each(boost::bind(predSetUserVolume, _1, id, clamped_volume)); } std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) @@ -1930,6 +1961,24 @@ std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) return result; } +//////////////////////// +///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)); + } +} + + BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() { return mAreaVoiceDisabled; @@ -2011,7 +2060,6 @@ void LLWebRTCVoiceClient::sessionState::reapEmptySessions() } } - bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) { ptr_t aLock(a.lock()); @@ -2153,7 +2201,6 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" << " mSession=" << mSession << LL_ENDL; - if(mSession) { if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) @@ -2531,6 +2578,29 @@ void LLVoiceWebRTCConnection::setSpeakerVolume(F32 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*100; + 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); +} + + void LLVoiceWebRTCConnection::sendData(const std::string &data) { if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) @@ -2825,6 +2895,8 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar 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); @@ -2842,6 +2914,16 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { joined = true; primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + 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] = volume; + } } new_participant |= joined; @@ -2869,6 +2951,21 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } } } + 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); + } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e1a740929b..d7d6ca6229 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -40,6 +40,7 @@ class LLWebRTCProtocolParser; #include "lleventcoro.h" #include "llcoros.h" #include "llparcel.h" +#include "llmutelist.h" #include #include "json/reader.h" @@ -60,7 +61,8 @@ typedef boost::shared_ptr connectionPtr_t; class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface, - public llwebrtc::LLWebRTCDevicesObserver + public llwebrtc::LLWebRTCDevicesObserver, + public LLMuteListObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); @@ -240,6 +242,13 @@ public: bool isPreviewRecording() override { return false; } bool isPreviewPlaying() override { return false; } + //@} + + ////////////////// + /// @name LLMuteListObserver + //@{ + void onChange() override; + void onChangeDetailed(const LLMute& ) override; //@} // authorize the user @@ -344,6 +353,9 @@ public: 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); @@ -454,6 +466,8 @@ public: 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); ////////////////////////////// /// @name TVC/Server management and communication @@ -765,7 +779,9 @@ class LLVoiceWebRTCConnection : 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(); -- cgit v1.2.3 From 9389532761ca3987a37a705d534f4ca62baf7697 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 3 Feb 2024 18:53:04 -0800 Subject: fix locking race condition --- indra/llwebrtc/llwebrtc.cpp | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index fca490e8c2..900698aa56 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -673,20 +673,24 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp) void LLWebRTCPeerConnectionImpl::setMute(bool mute) { mMute = mute; - if (mPeerConnection) - { - auto senders = mPeerConnection->GetSenders(); - - RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); - for (auto &sender : senders) + mWebRTCImpl->PostSignalingTask( + [this]() + { + if (mPeerConnection) { - auto track = sender->track(); - if(track) + auto senders = mPeerConnection->GetSenders(); + + RTC_LOG(LS_INFO) << __FUNCTION__ << (mMute ? "disabling" : "enabling") << " streams count " << senders.size(); + for (auto &sender : senders) { - track->set_enabled(!mMute); + auto track = sender->track(); + if (track) + { + track->set_enabled(!mMute); + } } } - } + }); } void LLWebRTCPeerConnectionImpl::resetMute() @@ -719,13 +723,17 @@ void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume) void LLWebRTCPeerConnectionImpl::setSendVolume(float volume) { - if (mLocalStream) - { - for (auto &track : mLocalStream->GetAudioTracks()) + mWebRTCImpl->PostSignalingTask( + [this, volume]() { - track->GetSource()->SetVolume(volume); - } - } + if (mLocalStream) + { + for (auto &track : mLocalStream->GetAudioTracks()) + { + track->GetSource()->SetVolume(volume); + } + } + }); } // -- cgit v1.2.3 From 9ac4334ff38a6aec26384fd37c33805105231928 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 3 Feb 2024 20:41:40 -0800 Subject: small logic errors in mute/volume for others code --- indra/newview/llvoicewebrtc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index e59e267d31..a3f874aacf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -918,6 +918,10 @@ void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 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); @@ -926,7 +930,7 @@ void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID& id, F32 volu void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID& id, bool mute) { - if (mParticipantsByUUID.find(id) != mParticipantsByUUID.end()) + if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) { return; } @@ -2582,7 +2586,7 @@ 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*100; + user_gain[id.asString()] = (uint32_t)(volume*100); root["ug"] = user_gain; Json::FastWriter writer; std::string json_data = writer.write(root); -- cgit v1.2.3 From 8d414e408e096946b0409e8ca9d5011d64f89671 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 3 Feb 2024 22:02:48 -0800 Subject: Handle 'device changed' callback --- indra/llwebrtc/llwebrtc.cpp | 22 +++++++++++++++++----- indra/llwebrtc/llwebrtc.h | 9 ++++++--- indra/llwebrtc/llwebrtc_impl.h | 7 ++++++- indra/newview/llvoicewebrtc.cpp | 27 ++++++++++++++++++++++++++- indra/newview/llvoicewebrtc.h | 1 - 5 files changed, 55 insertions(+), 11 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 900698aa56..d6eff5e1f2 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -113,6 +113,7 @@ void LLWebRTCImpl::init() mTuningDeviceModule->SetStereoRecording(true); mTuningDeviceModule->SetStereoPlayout(true); mTuningDeviceModule->EnableBuiltInAEC(false); + mTuningDeviceModule->SetAudioDeviceSink(this); updateDevices(); }); @@ -379,31 +380,42 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) void LLWebRTCImpl::updateDevices() { - int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); + 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); + renderDeviceList.emplace_back(name, guid, index == currentRenderDeviceIndex); } - int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); + 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); + captureDeviceList.emplace_back(name, guid, index == currentCaptureDeviceIndex); } for (auto &observer : mVoiceDevicesObserverList) { - observer->OnDevicesChanged(renderDeviceList, captureDeviceList); + observer->OnDevicesChanged(renderDeviceList, + captureDeviceList); } } +void LLWebRTCImpl::OnDevicesUpdated() +{ + updateDevices(); +} + + void LLWebRTCImpl::setTuningMode(bool enable) { mWorkerThread->BlockingCall( diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index f84e998245..e7effdfbb8 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -56,11 +56,14 @@ class LLWebRTCVoiceDevice { public: std::string display_name; // friendly value for the user - std::string id; // internal value for selection + std::string id; // internal value for selection + bool current; // current device - LLWebRTCVoiceDevice(const std::string &display_name, const std::string &id) : + LLWebRTCVoiceDevice(const std::string &display_name, const std::string &id, bool current) : display_name(display_name), - id(id) {}; + id(id), + current(current) + {}; }; typedef std::vector LLWebRTCVoiceDeviceList; diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 884e107527..3ccb6058ad 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -89,7 +89,7 @@ class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver float mMicrophoneEnergy; }; -class LLWebRTCImpl : public LLWebRTCDeviceInterface +class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceSink { public: LLWebRTCImpl() : @@ -117,6 +117,11 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface float getTuningAudioLevel() override; float getPeerAudioLevel() override; + // + // AudioDeviceSink + // + void OnDevicesUpdated() override; + // // Helpers // diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a3f874aacf..9304ce4740 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -8,7 +8,7 @@ * ne * 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; + * 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, @@ -689,16 +689,41 @@ void LLWebRTCVoiceClient::setDevicesListUpdated(bool 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.display_name, device.id)); + if (device.current && outputDevice == device.id) + { + setRenderDevice(outputDevice); + renderDeviceSet = true; + } + } + if (!renderDeviceSet) + { + setRenderDevice("Default"); } + clearCaptureDevices(); + bool captureDeviceSet = false; for (auto &device : capture_devices) { addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); + if (device.current && inputDevice == device.id) + { + setCaptureDevice(outputDevice); + captureDeviceSet = true; + } } + if (!captureDeviceSet) + { + setCaptureDevice("Default"); + } + setDevicesListUpdated(true); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index d7d6ca6229..83cb3c7eef 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -102,7 +102,6 @@ public: /// @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. bool deviceSettingsAvailable() override; bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. -- cgit v1.2.3 From 8df93ef7858e77f40242f650dffac5770cf4eede Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 4 Feb 2024 15:49:27 -0800 Subject: Add server-generate VAD --- indra/newview/llvoicewebrtc.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9304ce4740..c70a60748d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2951,7 +2951,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 volume; if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume)) { - user_gain[participant_id] = volume; + user_gain[participant_id] = uint16_t(volume*200); } } @@ -2974,9 +2974,11 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; // convert to decibles participant->mLevel = level; - /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we - can use it at some point when we actually process frames. */ - participant->mIsSpeaking = participant->mLevel > SPEAKING_AUDIO_LEVEL; + + if (voice_data["participant_id"].isMember("v")) + { + participant->mIsSpeaking = voice_data[participant_id].get("v", Json::Value(false)).asBool(); + } } } } -- cgit v1.2.3 From 534f565a1e89ce98beb1e378b984e6aaa9c1fa6e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 4 Feb 2024 15:51:07 -0800 Subject: Fix initial user gain send on join --- indra/newview/llvoicewebrtc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index c70a60748d..a6e26644b0 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -5,7 +5,7 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * ne + * * 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 @@ -2611,7 +2611,7 @@ 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*100); + 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); @@ -2951,7 +2951,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 volume; if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume)) { - user_gain[participant_id] = uint16_t(volume*200); + user_gain[participant_id] = (uint16_t)(volume*200); } } -- cgit v1.2.3 From dfa77d942c52ddf55942afb497a64fbb1d2fccc0 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 5 Feb 2024 14:42:21 -0800 Subject: Use a custom audio processor to pull data for level determinations, which will happen after AGC --- indra/llwebrtc/llwebrtc.cpp | 83 +++++++++++++++++++++++++----------------- indra/llwebrtc/llwebrtc_impl.h | 37 +++++++++---------- 2 files changed, 68 insertions(+), 52 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index d6eff5e1f2..f0d7f6ad8f 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -27,6 +27,7 @@ #include "llwebrtc_impl.h" #include #include +#include #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_encoder_factory.h" @@ -34,30 +35,54 @@ #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 { -const float VOLUME_SCALE_WEBRTC = 100; - -LLAudioDeviceObserver::LLAudioDeviceObserver() : mMicrophoneEnergy(0.0), mSumVector {0} {} +LLCustomProcessor::LLCustomProcessor() : + mSampleRateHz(0), + mNumChannels(0) +{ + memset(mSumVector, sizeof(mSumVector), 0); +} -float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } +void LLCustomProcessor::Initialize(int sample_rate_hz, int num_channels) +{ + mSampleRateHz = sample_rate_hz; + mNumChannels = num_channels; + memset(mSumVector, sizeof(mSumVector), 0); +} -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) +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 frame; + std::vector frame_samples; + + if (audio_in->num_channels() < 1 || audio_in->num_frames() < 480) + { + return; + } + + 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]); + float energy = 0; - const short *samples = (const short *) audio_samples; - for (size_t index = 0; index < num_samples * num_channels; index++) + for (size_t index = 0; index < stream_config.num_samples(); index++) { - float sample = (static_cast(samples[index]) / (float) 32767); + float sample = frame_samples[index]; energy += sample * sample; } - + // smooth it. size_t buffer_size = sizeof(mSumVector) / sizeof(mSumVector[0]); float totalSum = 0; @@ -69,15 +94,7 @@ void LLAudioDeviceObserver::OnCaptureData(const void *audio_samples, } 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) -{ + mMicrophoneEnergy = std::sqrt(totalSum / (stream_config.num_samples() * buffer_size)); } void LLWebRTCImpl::init() @@ -104,11 +121,9 @@ void LLWebRTCImpl::init() mWorkerThread->PostTask( [this]() { - mTuningAudioDeviceObserver = new LLAudioDeviceObserver; - mTuningDeviceModule = - webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, - mTaskQueueFactory.get(), - std::unique_ptr(mTuningAudioDeviceObserver)); + mTuningDeviceModule = webrtc::CreateAudioDeviceWithDataObserver( + webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, + mTaskQueueFactory.get(), nullptr); mTuningDeviceModule->Init(); mTuningDeviceModule->SetStereoRecording(true); mTuningDeviceModule->SetStereoPlayout(true); @@ -134,11 +149,9 @@ void LLWebRTCImpl::init() mWorkerThread->BlockingCall( [this]() { - mPeerAudioDeviceObserver = new LLAudioDeviceObserver; mPeerDeviceModule = webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, - mTaskQueueFactory.get(), - std::unique_ptr(mPeerAudioDeviceObserver)); + mTaskQueueFactory.get(), nullptr); mPeerDeviceModule->Init(); mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); @@ -151,7 +164,11 @@ void LLWebRTCImpl::init() mPeerDeviceModule->InitPlayout(); }); - rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); + mCustomProcessor = new LLCustomProcessor; + webrtc::AudioProcessingBuilder apb; + apb.SetCapturePostProcessing(std::unique_ptr(mCustomProcessor)); + rtc::scoped_refptr apm = apb.Create(); + webrtc::AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = true; apm_config.echo_canceller.mobile_mode = false; @@ -454,9 +471,9 @@ void LLWebRTCImpl::setTuningMode(bool enable) } } -float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mTuningAudioDeviceObserver->getMicrophoneEnergy()); } +float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mCustomProcessor->getMicrophoneEnergy()); } -float LLWebRTCImpl::getPeerAudioLevel() { return -20 * log10f(mPeerAudioDeviceObserver->getMicrophoneEnergy()); } +float LLWebRTCImpl::getPeerAudioLevel() { return -20 * log10f(mCustomProcessor->getMicrophoneEnergy()); } // // Helpers diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 3ccb6058ad..419482b751 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -65,27 +65,27 @@ namespace llwebrtc class LLWebRTCPeerConnectionImpl; -class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver +class LLCustomProcessor : public webrtc::CustomProcessing { public: - LLAudioDeviceObserver(); + LLCustomProcessor(); + ~LLCustomProcessor() override {} - float getMicrophoneEnergy(); + // (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 ""; } - 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; - - 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; + float getMicrophoneEnergy() { return mMicrophoneEnergy; } protected: - float mSumVector[30]; // 300 ms of smoothing + static const int NUM_PACKETS_TO_FILTER = 30; // 300 ms of smoothing + int mSampleRateHz; + int mNumChannels; + + float mSumVector[NUM_PACKETS_TO_FILTER]; float mMicrophoneEnergy; }; @@ -93,7 +93,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS { public: LLWebRTCImpl() : - mTuningAudioDeviceObserver(nullptr), mPeerAudioDeviceObserver(nullptr), mMute(true) + mCustomProcessor(nullptr), mMute(true) { } ~LLWebRTCImpl() {} @@ -189,9 +189,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS int32_t mPlayoutDevice; int32_t mRecordingDevice; bool mMute; - - LLAudioDeviceObserver * mTuningAudioDeviceObserver; - LLAudioDeviceObserver * mPeerAudioDeviceObserver; + + LLCustomProcessor * mCustomProcessor; // peer connections std::vector> mPeerConnections; -- cgit v1.2.3 From e4c3ca53188152bc12a75f807eabef3dc5e9248b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 5 Feb 2024 16:47:47 -0800 Subject: put observer-based tuning audio level calculation back --- indra/llwebrtc/llwebrtc.cpp | 53 +++++++++++++++++++++++++++++++++++++----- indra/llwebrtc/llwebrtc_impl.h | 29 +++++++++++++++++++++-- 2 files changed, 74 insertions(+), 8 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index f0d7f6ad8f..3273786242 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -40,6 +40,46 @@ namespace llwebrtc { +LLAudioDeviceObserver::LLAudioDeviceObserver() : mMicrophoneEnergy(0.0), mSumVector {0} {} + +float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } + +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) +{ + 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(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) @@ -118,12 +158,13 @@ void LLWebRTCImpl::init() mSignalingThread->SetName("WebRTCSignalingThread", nullptr); mSignalingThread->Start(); + mTuningAudioDeviceObserver = new LLAudioDeviceObserver; mWorkerThread->PostTask( [this]() { mTuningDeviceModule = webrtc::CreateAudioDeviceWithDataObserver( - webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, - mTaskQueueFactory.get(), nullptr); + webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, mTaskQueueFactory.get(), + std::unique_ptr(mTuningAudioDeviceObserver)); mTuningDeviceModule->Init(); mTuningDeviceModule->SetStereoRecording(true); mTuningDeviceModule->SetStereoPlayout(true); @@ -164,9 +205,9 @@ void LLWebRTCImpl::init() mPeerDeviceModule->InitPlayout(); }); - mCustomProcessor = new LLCustomProcessor; + mPeerCustomProcessor = new LLCustomProcessor; webrtc::AudioProcessingBuilder apb; - apb.SetCapturePostProcessing(std::unique_ptr(mCustomProcessor)); + apb.SetCapturePostProcessing(std::unique_ptr(mPeerCustomProcessor)); rtc::scoped_refptr apm = apb.Create(); webrtc::AudioProcessing::Config apm_config; @@ -471,9 +512,9 @@ void LLWebRTCImpl::setTuningMode(bool enable) } } -float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mCustomProcessor->getMicrophoneEnergy()); } +float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mTuningAudioDeviceObserver->getMicrophoneEnergy()); } -float LLWebRTCImpl::getPeerAudioLevel() { return -20 * log10f(mCustomProcessor->getMicrophoneEnergy()); } +float LLWebRTCImpl::getPeerAudioLevel() { return -20 * log10f(mPeerCustomProcessor->getMicrophoneEnergy()); } // // Helpers diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 419482b751..7146267379 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -65,6 +65,30 @@ namespace llwebrtc class LLWebRTCPeerConnectionImpl; +class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver +{ + public: + LLAudioDeviceObserver(); + + float getMicrophoneEnergy(); + + 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; + + 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: + float mSumVector[30]; // 300 ms of smoothing + float mMicrophoneEnergy; +}; + class LLCustomProcessor : public webrtc::CustomProcessing { public: @@ -93,7 +117,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS { public: LLWebRTCImpl() : - mCustomProcessor(nullptr), mMute(true) + mPeerCustomProcessor(nullptr), mMute(true) { } ~LLWebRTCImpl() {} @@ -190,7 +214,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS int32_t mRecordingDevice; bool mMute; - LLCustomProcessor * mCustomProcessor; + LLAudioDeviceObserver * mTuningAudioDeviceObserver; + LLCustomProcessor * mPeerCustomProcessor; // peer connections std::vector> mPeerConnections; -- cgit v1.2.3 From 5a9f0488f458ade64e3a3f2388d5cd23092eed5d Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 5 Feb 2024 16:48:43 -0800 Subject: mac build fix --- indra/llwebrtc/llwebrtc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 3273786242..06a5c3a085 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -84,14 +84,14 @@ LLCustomProcessor::LLCustomProcessor() : mSampleRateHz(0), mNumChannels(0) { - memset(mSumVector, sizeof(mSumVector), 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, sizeof(mSumVector), 0); + memset(mSumVector, 0, sizeof(mSumVector)); } void LLCustomProcessor::Process(webrtc::AudioBuffer *audio_in) -- cgit v1.2.3 From 6f4ee11ae942d331fcf57db7466989f90bb55ccf Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 6 Feb 2024 19:12:14 -0800 Subject: race between session established and data channel ready --- indra/llwebrtc/llwebrtc.cpp | 48 +++++++++++++---------------------------- indra/newview/llvoicewebrtc.cpp | 43 +++++++++++++++++++++++++----------- indra/newview/llvoicewebrtc.h | 15 +++++++------ 3 files changed, 53 insertions(+), 53 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 06a5c3a085..ee85a254f2 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -476,40 +476,22 @@ void LLWebRTCImpl::OnDevicesUpdated() void LLWebRTCImpl::setTuningMode(bool enable) { - mWorkerThread->BlockingCall( - [this, enable]() - { - if (enable) - { - - mTuningDeviceModule->StartRecording(); - - if (mPeerDeviceModule) - { - mPeerDeviceModule->StopRecording(); - } - } - else - { - mTuningDeviceModule->StartRecording(); - if (mPeerDeviceModule) - { - mPeerDeviceModule->StartRecording(); - } - } - }); - for (auto& connection : mPeerConnections) - { - if (enable) + mSignalingThread->PostTask( + [this, enable] { - connection->enableSenderTracks(false); - } - else - { - connection->resetMute(); - } - connection->enableReceiverTracks(!enable); - } + for (auto &connection : mPeerConnections) + { + if (enable) + { + connection->enableSenderTracks(false); + } + else + { + connection->resetMute(); + } + connection->enableReceiverTracks(!enable); + } + }); } float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mTuningAudioDeviceObserver->getMicrophoneEnergy()); } diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a6e26644b0..7b85795697 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2843,9 +2843,23 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); mWebRTCAudioInterface->setSendVolume(mMicGain); LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID); - setVoiceConnectionState(VOICE_STATE_SESSION_UP); + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL); + break; + } + case VOICE_STATE_WAIT_FOR_DATA_CHANNEL: + { + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + break; + } + if (mWebRTCDataInterface) + { + sendJoin(); + setVoiceConnectionState(VOICE_STATE_SESSION_UP); + } + break; } - break; case VOICE_STATE_SESSION_UP: { if (mShutDown) @@ -3006,19 +3020,22 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface { mWebRTCDataInterface = data_interface; mWebRTCDataInterface->setDataObserver(this); + } +} - Json::FastWriter writer; - Json::Value root = Json::objectValue; - Json::Value join_obj = Json::objectValue; - LLUUID regionID = gAgent.getRegion()->getRegionID(); - if (regionID == mRegionID) - { - join_obj["p"] = true; - } - root["j"] = join_obj; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); +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) + { + join_obj["p"] = true; } + root["j"] = join_obj; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); } ///////////////////////////// diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 83cb3c7eef..16bcadb144 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -769,6 +769,7 @@ class LLVoiceWebRTCConnection : void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; //@} + void sendJoin(); void sendData(const std::string &data); virtual void processIceUpdates(); @@ -806,13 +807,13 @@ class LLVoiceWebRTCConnection : VOICE_STATE_REQUEST_CONNECTION = 0x4, VOICE_STATE_CONNECTION_WAIT = 0x8, VOICE_STATE_SESSION_ESTABLISHED = 0x10, - VOICE_STATE_SESSION_UP = 0x20, - VOICE_STATE_SESSION_RETRY = 0x40, - VOICE_STATE_DISCONNECT = 0x80, - VOICE_STATE_WAIT_FOR_EXIT = 0x100, - VOICE_STATE_SESSION_EXIT = 0x200, - VOICE_STATE_SESSION_STOPPING = 0x3c0, - VOICE_STATE_SESSION_WAITING = 0x10a + 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; -- cgit v1.2.3 From 76497bbc15867d558f19e173291addb8e48ff433 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 7 Feb 2024 13:22:28 -0800 Subject: P2P checkpoint --- indra/newview/llimview.cpp | 11 +++++++---- indra/newview/llimview.h | 3 ++- indra/newview/llvoicechannel.cpp | 11 ++++++----- indra/newview/llvoicechannel.h | 3 ++- indra/newview/llvoicewebrtc.cpp | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) (limited to 'indra') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 78017337e8..a6eeaeffc6 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -674,8 +674,6 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& } else { - mVoiceChannel = new LLVoiceChannelGroup(session_id, name); - // determine whether it is group or conference session if (gAgent.isInGroup(mSessionID)) { @@ -690,6 +688,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& // webrtc uses adhoc channels for p2p mSessionType = P2P_SESSION; } + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, mSessionType == P2P_SESSION); } if(mVoiceChannel) @@ -3432,7 +3431,8 @@ void LLIMMgr::inviteToSession( EInstantMessage type, EInvitationType inv_type, const std::string& session_handle, - const std::string& session_uri) + const std::string& session_uri, + const std::string& voice_server_type) { std::string notify_box_type; // voice invite question is different from default only for group call (EXT-7118) @@ -4153,12 +4153,15 @@ public: return; } + std::string voice_server_type = input["body"]["voice"].get("voice_server_type").asString(); + BOOL session_type_p2p = input["body"]["voice"].get("p2p").asBoolean(); + 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, + session_type_p2p ? IM_SESSION_P2P_INVITE : IM_SESSION_INVITE, LLIMMgr::INVITATION_TYPE_VOICE); } else if ( input["body"].has("immediate") ) diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 946eb02f26..49c73bd495 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -417,7 +417,8 @@ public: EInstantMessage type, EInvitationType inv_type, const std::string& session_handle = LLStringUtil::null, - const std::string& session_uri = LLStringUtil::null); + const std::string& session_uri = LLStringUtil::null, + const std::string& voice_server_type = LLStringUtil::null); 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/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 05c22dd645..4ca876bed1 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -410,8 +410,9 @@ 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 hangup_on_last_leave) : + LLVoiceChannel(session_id, session_name), + mHangupOnLastLeave(hangup_on_last_leave) { mRetries = DEFAULT_RETRIES_COUNT; mIsRetrying = FALSE; @@ -438,7 +439,7 @@ void LLVoiceChannelGroup::activate() LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, mCredentials, - !LLVoiceClient::getInstance()->hasP2PInterface()); + mHangupOnLastLeave); if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel { @@ -520,7 +521,7 @@ void LLVoiceChannelGroup::setChannelInfo( LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, mCredentials, - !LLVoiceClient::getInstance()->hasP2PInterface()); + mHangupOnLastLeave); } } @@ -792,7 +793,7 @@ void LLVoiceChannelProximal::deactivate() // LLVoiceChannelP2P // LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : - LLVoiceChannelGroup(session_id, session_name), + LLVoiceChannelGroup(session_id, session_name, true), mOtherUserID(other_user_id), mReceivedCall(FALSE) { diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 309c3eebdd..84ef3e7c08 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -144,7 +144,7 @@ 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 hangup_on_last_leave); /*virtual*/ void handleStatusChange(EStatusType status); /*virtual*/ void handleError(EStatusType status); @@ -163,6 +163,7 @@ private: U32 mRetries; BOOL mIsRetrying; + BOOL mHangupOnLastLeave; }; class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 7b85795697..399db275ab 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -3029,7 +3029,7 @@ void LLVoiceWebRTCConnection::sendJoin() Json::Value root = Json::objectValue; Json::Value join_obj = Json::objectValue; LLUUID regionID = gAgent.getRegion()->getRegionID(); - if (regionID == mRegionID) + if ((regionID == mRegionID) || !isSpatial()) { join_obj["p"] = true; } -- cgit v1.2.3 From 63a4a83c1b3b8168691015fde0fd88a369c934a6 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 12:40:56 -0800 Subject: Add new P@P multiagentchat handler for webrtc voice --- indra/newview/llimview.cpp | 63 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) (limited to 'indra') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index a6eeaeffc6..de1422829b 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -87,7 +87,10 @@ 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); +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); @@ -396,7 +399,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); @@ -437,6 +440,35 @@ 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 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 conference" << 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); @@ -2041,8 +2073,7 @@ bool LLIMModel::sendStartSession( return true; } - else if (( dialog == IM_SESSION_CONFERENCE_START ) || - (((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) && !LLVoiceClient::getInstance()->hasP2PInterface())) + else if (dialog == IM_SESSION_CONFERENCE_START ) { LLSD agents; for (int i = 0; i < (S32) ids.size(); i++) @@ -2057,8 +2088,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 @@ -2073,6 +2104,26 @@ 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)) && !LLVoiceClient::getInstance()->hasP2PInterface()) + { + LLSD agents; + for (int i = 0; i < (S32) ids.size(); i++) + { + agents.append(ids[i]); + } + + // we have a new way of starting conference calls now + 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; } -- cgit v1.2.3 From 56b05eb9a1daef2927579408dc68dded7d0226cb Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 18:48:36 -0800 Subject: fix rebase issue --- indra/llwebrtc/llwebrtc.cpp | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index ee85a254f2..768405b75f 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -173,20 +173,6 @@ void LLWebRTCImpl::init() updateDevices(); }); - rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); - 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; - mWorkerThread->BlockingCall( [this]() { -- cgit v1.2.3 From 55b13f5630e240063a15d43ce7f49a88b8015e6d Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 19:00:22 -0800 Subject: rebase merge fix --- indra/newview/llvoicevivox.h | 138 +++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 69 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 6e6ffdb1e4..5ddb72fa24 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -56,7 +56,7 @@ class LLVivoxVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface { - LLSINGLETON(LLVivoxVoiceClient); + LLSINGLETON_C11(LLVivoxVoiceClient); LOG_CLASS(LLVivoxVoiceClient); virtual ~LLVivoxVoiceClient(); @@ -64,26 +64,26 @@ public: /// @name LLVoiceModuleInterface virtual implementations /// @see LLVoiceModuleInterface //@{ - virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - virtual void terminate(); // Call this to clean up during shutdown + void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) + void terminate() override; // Call this to clean up during shutdown - virtual const LLVoiceVersionInfo& getVersion(); + const LLVoiceVersionInfo& getVersion() override; - virtual void updateSettings(); // call after loading settings and whenever they change + 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; + bool isVoiceWorking() const override; ///////////////////// /// @name Tuning //@{ - virtual void tuningStart(); - virtual void tuningStop(); - virtual bool inTuningMode(); + void tuningStart() override; + void tuningStop() override; + bool inTuningMode() override; - virtual void tuningSetMicVolume(float volume); - virtual void tuningSetSpeakerVolume(float volume); - virtual float tuningGetEnergy(void); + void tuningSetMicVolume(float volume) override; + void tuningSetSpeakerVolume(float volume) override; + float tuningGetEnergy(void) override; //@} ///////////////////// @@ -91,40 +91,40 @@ public: //@{ // 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(); - virtual bool deviceSettingsUpdated(); //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); + void refreshDeviceLists(bool clearCurrentList = true) override; - virtual void setCaptureDevice(const std::string& name); - virtual void setRenderDevice(const std::string& name); + void setCaptureDevice(const std::string& name) override; + void setRenderDevice(const std::string& name) override; - virtual LLVoiceDeviceList& getCaptureDevices(); - virtual LLVoiceDeviceList& getRenderDevices(); - //@} + LLVoiceDeviceList& getCaptureDevices() override; + LLVoiceDeviceList& getRenderDevices() override; + //@} - virtual void getParticipantList(std::set &participants); - virtual bool isParticipant(const LLUUID& speaker_id); + void getParticipantList(std::set &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); + 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); + 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); + BOOL isSessionTextIMPossible(const LLUUID &session_id) override; //////////////////////////// @@ -132,22 +132,22 @@ public: //@{ // 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(); + bool inProximalChannel() override; void setNonSpatialChannel(const std::string &uri, const std::string &credentials, bool hangup_on_last_leave) override; - virtual bool setSpatialChannel(const std::string &uri, - const std::string &credentials); + bool setSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual void leaveNonSpatialChannel(); + void leaveNonSpatialChannel() override; - virtual void leaveChannel(void); + 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(); + std::string getCurrentChannel() override; //@} @@ -156,7 +156,7 @@ public: //@{ // start a voice channel with the specified user bool hasP2PInterface() override { return true; } - void callUser(const LLUUID &uuid); + void callUser(const LLUUID &uuid) override; bool isValidChannel(std::string &channelHandle) override; bool answerInvite(std::string &channelHandle) override; void declineInvite(std::string &channelHandle) override; @@ -172,43 +172,43 @@ public: ///////////////////////// /// @name enable disable voice and features //@{ - virtual bool voiceEnabled(); - virtual void setVoiceEnabled(bool enabled); - virtual BOOL lipSyncEnabled(); - virtual void setLipSyncEnabled(BOOL enabled); - virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + bool voiceEnabled() override; + void setVoiceEnabled(bool enabled) override; + BOOL lipSyncEnabled() override; + void setLipSyncEnabled(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); // true if we've received data for this avatar - virtual std::string getDisplayName(const LLUUID& id); - virtual BOOL isParticipantAvatar(const LLUUID &id); - virtual BOOL getIsSpeaking(const LLUUID& id); - virtual BOOL getIsModeratorMuted(const LLUUID& id); - virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - virtual BOOL getOnMuteList(const LLUUID& id); - virtual F32 getUserVolume(const LLUUID& id); - virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar + 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... + BOOL getOnMuteList(const LLUUID& id) override; + 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); + const LLUUID &agentID) override; ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceClientStatusObserver* observer); - virtual void removeObserver(LLVoiceClientStatusObserver* observer); - virtual void addObserver(LLFriendObserver* observer); - virtual void removeObserver(LLFriendObserver* observer); - virtual void addObserver(LLVoiceClientParticipantObserver* observer); - virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + 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); + std::string sipURIFromID(const LLUUID &id) override; //@} /// @name LLVoiceEffectInterface virtual implementations @@ -218,32 +218,32 @@ public: ////////////////////////// /// @name Accessors //@{ - virtual bool setVoiceEffect(const LLUUID& id); - virtual const LLUUID getVoiceEffect(); - virtual LLSD getVoiceEffectProperties(const LLUUID& id); + bool setVoiceEffect(const LLUUID& id) override; + const LLUUID getVoiceEffect() override; + LLSD getVoiceEffectProperties(const LLUUID& id) override; - virtual void refreshVoiceEffectLists(bool clear_lists); - virtual const voice_effect_list_t& getVoiceEffectList() const; - virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + 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); - virtual void removeObserver(LLVoiceEffectObserver* observer); + void addObserver(LLVoiceEffectObserver* observer) override; + void removeObserver(LLVoiceEffectObserver* observer) override; //@} ////////////////////////////// /// @name Effect preview buffer //@{ - virtual void enablePreviewBuffer(bool enable); - virtual void recordPreviewBuffer(); - virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); - virtual void stopPreviewBuffer(); + void enablePreviewBuffer(bool enable) override; + void recordPreviewBuffer() override; + void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; + void stopPreviewBuffer() override; - virtual bool isPreviewRecording(); - virtual bool isPreviewPlaying(); + bool isPreviewRecording() override; + bool isPreviewPlaying() override; //@} //@} @@ -752,7 +752,7 @@ private: std::string getAudioSessionURI(); std::string getAudioSessionHandle(); - void setHidden(bool hidden); //virtual + void setHidden(bool hidden) override; //virtual void sendPositionAndVolumeUpdate(void); void sendCaptureAndRenderDevices(); -- cgit v1.2.3 From c827f14f0270a0954fd33e165c2ba94459d9c116 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 19:02:13 -0800 Subject: another rebase merge issue --- indra/llwebrtc/llwebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 768405b75f..283124e315 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -970,7 +970,7 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface * 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-maxcapturerate=48000;sprop-maxplaybackrate=48000\n"; + << " minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1;maxplaybackrate=48000;sprop-maxplaybackrate=48000;sprop-maxcapturerate=48000\n"; } else { -- cgit v1.2.3 From 1fc6ea0f25f52da56316d038e39c5add5e58747e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 19:04:01 -0800 Subject: another rebase merge issue --- indra/newview/llvoicewebrtc.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 399db275ab..df3ca08955 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1887,7 +1887,7 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { - F32 result = 0; + F32 result = 0.0; if (!mSession) { return result; @@ -1895,7 +1895,10 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(mSession->findParticipant(id.asString())); if (participant) { - result = participant->mLevel; + if (participant->mIsSpeaking) + { + result = participant->mLevel; + } } return result; } @@ -2965,7 +2968,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 volume; if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume)) { - user_gain[participant_id] = (uint16_t)(volume*200); + user_gain[participant_id] = (uint32_t)(volume * 200); } } @@ -2989,7 +2992,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar // convert to decibles participant->mLevel = level; - if (voice_data["participant_id"].isMember("v")) + if (voice_data[participant_id].isMember("v")) { participant->mIsSpeaking = voice_data[participant_id].get("v", Json::Value(false)).asBool(); } -- cgit v1.2.3 From 707d76880a2a1dedd0b9045e1954eff8fbf26d3e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 9 Feb 2024 13:13:44 -0800 Subject: Fix ad-hoc voice --- indra/newview/llimview.cpp | 29 ++++++++++++++++------------- indra/newview/llvoicewebrtc.cpp | 4 +++- 2 files changed, 19 insertions(+), 14 deletions(-) (limited to 'indra') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index de1422829b..9b2cd46a4b 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -697,12 +697,19 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& // set P2P type by default mSessionType = P2P_SESSION; - if ((IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) && LLVoiceClient::getInstance()->hasP2PInterface()) - { - // 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); + if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) + { + if (LLVoiceClient::getInstance()->hasP2PInterface()) + { + // 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); + } + else + { + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); + } } else { @@ -710,17 +717,13 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& if (gAgent.isInGroup(mSessionID)) { mSessionType = GROUP_SESSION; + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); } - else if (LLVoiceClient::getInstance()->hasP2PInterface()) + else { mSessionType = ADHOC_SESSION; + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); } - else - { - // webrtc uses adhoc channels for p2p - mSessionType = P2P_SESSION; - } - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, mSessionType == P2P_SESSION); } if(mVoiceChannel) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index df3ca08955..d3b9f8ba2c 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -531,7 +531,9 @@ bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() // add new connections for new neighbors for (auto &neighbor : neighbor_ids) { - connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + + mWebRTCConnections.push_back(connection); connection->setMicGain(mMicGain); connection->setMuteMic(mMuted); connection->setSpeakerVolume(mSpeakerVolume); -- cgit v1.2.3 From cefe1789bcb3f8cc10af7c69872295ef8d77f22f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 22 Feb 2024 14:34:03 -0800 Subject: For spatial voice, determine voice provider based on server setting. --- indra/newview/llappviewer.cpp | 8 +-- indra/newview/llimview.cpp | 5 +- indra/newview/llviewerregion.h | 2 +- indra/newview/llvoiceclient.cpp | 132 +++++++++++++++++++++++++++++----------- indra/newview/llvoiceclient.h | 17 +++++- indra/newview/llvoicevivox.cpp | 29 ++++++++- indra/newview/llvoicewebrtc.cpp | 22 ++++++- 7 files changed, 166 insertions(+), 49 deletions(-) (limited to 'indra') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 4a43133ff6..9691bb9a8c 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(); } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 9b2cd46a4b..1c8cdfd641 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -4216,7 +4216,10 @@ public: input["body"]["from_id"].asUUID(), input["body"]["from_name"].asString(), session_type_p2p ? IM_SESSION_P2P_INVITE : IM_SESSION_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE); + LLIMMgr::INVITATION_TYPE_VOICE, + LLStringUtil::null, // session_handle + LLStringUtil::null, + voice_server_type); } else if ( input["body"].has("immediate") ) { diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index b24ff51479..5817bd7d8d 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -546,7 +546,7 @@ public: U8 mCentralBakeVersion; LLVOCacheEntry* mLastVisitedEntry; - U32 mInvisibilityCheckHistory; + U32 mInvisibilityCheckHistory; // Information for Homestead / CR-53 S32 mClassID; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index de7fb248b0..3532d8e986 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -133,7 +133,6 @@ LLVoiceClient::LLVoiceClient(LLPumpIO *pump) mMuteMic(false), mDisableMic(false) { - updateSettings(); init(pump); } @@ -149,31 +148,71 @@ 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") - { - mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance(); - } - if (voice_server == "webrtc") + mUserID = user_id; + mAgentID = agentID; + gAgent.addRegionChangedCallback(boost::bind(&LLVoiceClient::onRegionChanged, this)); +} + +void LLVoiceClient::onRegionChanged() +{ + LLViewerRegion *region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + } + else if (region) + { + if (mSimulatorFeaturesReceivedSlot.connected()) + { + mSimulatorFeaturesReceivedSlot.disconnect(); + } + mSimulatorFeaturesReceivedSlot = + region->setSimulatorFeaturesReceivedCallback( + boost::bind(&LLVoiceClient::onSimulatorFeaturesReceived, this, _1)); + } +} + +void LLVoiceClient::onSimulatorFeaturesReceived(const LLUUID& region_id) +{ + LLViewerRegion *region = gAgent.getRegion(); + if (region && (region->getRegionID() == region_id)) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + } +} + +void LLVoiceClient::setVoiceModule(const std::string& voice_server_type) +{ + if (voice_server_type == "vivox" || voice_server_type.empty()) + { + mVoiceModule = (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance(); + } + else if (voice_server_type == "webrtc") { mVoiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); } else { - mVoiceModule = NULL; - return; + LLNotificationsUtil::add("VoiceVersionMismatch"); + mVoiceModule = nullptr; + return; } - mVoiceModule->init(m_servicePump); - mVoiceModule->userAuthorized(user_id, agentID); + mVoiceModule->userAuthorized(mUserID, mAgentID); + updateSettings(); } + + void LLVoiceClient::setHidden(bool hidden) { if (mVoiceModule) @@ -205,7 +244,7 @@ const LLVoiceVersionInfo LLVoiceClient::getVersion() { LLVoiceVersionInfo result; result.serverVersion = std::string(); - result.serverType = std::string(); + result.voiceServerType = std::string(); result.mBuildVersion = std::string(); return result; } @@ -864,31 +903,54 @@ 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")) { - 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) - { - if (!sAlertedUser) - { - //sAlertedUser = TRUE; - LLNotificationsUtil::add("VoiceVersionMismatch"); - gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener - } - } + voice_server_type = input["body"]["voice_server_type"].asString(); + } + + LLVoiceModuleInterface *voiceModule = NULL; + + if (voice_server_type == "vivox" || voice_server_type.empty()) + { + 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) + { + // sAlertedUser = true; + LLNotificationsUtil::add("VoiceVersionMismatch"); + LL_WARNS("Voice") << "Voice server version mismatch " << input["body"]["major_version"].asInteger() << "/" + << versionInfo.majorVersion + << LL_ENDL; + } + return; } } }; @@ -1099,7 +1161,7 @@ void LLSpeakerVolumeStorage::save() } } -BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE; +bool LLViewerRequiredVoiceVersion::sAlertedUser = false; LLHTTPRegistration gHTTPRegistrationMessageParcelVoiceInfo( diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 69004f2000..4ed3cd5066 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -94,8 +94,10 @@ public: struct LLVoiceVersionInfo { - std::string serverType; - std::string serverVersion; + std::string voiceServerType; + int majorVersion; + int minorVersion; + std::string serverVersion; std::string mBuildVersion; }; @@ -122,6 +124,8 @@ public: virtual void setHidden(bool hidden)=0; // Hides the user from voice. virtual const LLVoiceVersionInfo& getVersion()=0; + + ///////////////////// /// @name Tuning @@ -449,9 +453,13 @@ public: void endUserIMSession(const LLUUID &uuid); //@} + void setVoiceModule(const std::string& voice_server_type); void userAuthorized(const std::string& user_id, - const LLUUID &agentID); + const LLUUID &agentID); + + void onRegionChanged(); + void onSimulatorFeaturesReceived(const LLUUID ®ion_id); void addObserver(LLVoiceClientStatusObserver* observer); void removeObserver(LLVoiceClientStatusObserver* observer); @@ -478,6 +486,9 @@ protected: LLVoiceModuleInterface* mVoiceModule; LLPumpIO *m_servicePump; + std::string mUserID; + LLUUID mAgentID; + boost::signals2::connection mSimulatorFeaturesReceivedSlot; LLCachedControl mVoiceEffectEnabled; LLCachedControl mVoiceEffectDefault; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 3c01f00596..d708870314 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -85,7 +85,8 @@ namespace { const F32 SPEAKING_TIMEOUT = 1.f; - static const std::string VOICE_SERVER_TYPE = "Vivox"; + static const std::string VISIBLE_VOICE_SERVER_TYPE = "Vivox"; + static const std::string VIVOX_VOICE_SERVER_TYPE = "vivox"; // Don't retry connecting to the daemon more frequently than this: const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; @@ -358,7 +359,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mSpeakerVolume = scale_speaker_volume(0); mVoiceVersion.serverVersion = ""; - mVoiceVersion.serverType = VOICE_SERVER_TYPE; + mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; // gMuteListp isn't set up at this point, so we defer this until later. // gMuteListp->addObserver(&mutelist_listener); @@ -737,6 +738,19 @@ void LLVivoxVoiceClient::voiceControlStateMachine(S32 &coro_state) return; } + + // grab the active voice server version info to see if we should use + // vivox. + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) + { + // we've switched to another voice provider, so + // disable voice and shut down vivox. Don't + // notify, though. + mVoiceEnabled = false; + } + + switch (coro_state) { case VOICE_STATE_TP_WAIT: @@ -1972,6 +1986,17 @@ bool LLVivoxVoiceClient::waitForChannel() return false; } + // grab the active voice server version info to see if we should use + // vivox. + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) + { + // we've switched to another voice provider, so + // disable voice and shut down vivox. Don't + // notify, though. + mVoiceEnabled = false; + } + switch (state) { case VOICE_CHANNEL_STATE_LOGIN: diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d3b9f8ba2c..0332557229 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -93,7 +93,8 @@ namespace { const F32 SPEAKING_TIMEOUT = 1.f; const F32 SPEAKING_AUDIO_LEVEL = 0.40; - static const std::string VOICE_SERVER_TYPE = "WebRTC"; + static const std::string VISIBLE_VOICE_SERVER_TYPE = "WebRTC"; + static const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc"; // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.1f; @@ -289,7 +290,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mSpeakerVolume = 0.0; mVoiceVersion.serverVersion = ""; - mVoiceVersion.serverType = VOICE_SERVER_TYPE; + mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; #if LL_DARWIN || LL_LINUX // HACK: THIS DOES NOT BELONG HERE @@ -559,10 +560,24 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() continue; } + LLViewerRegion *regionp = gAgent.getRegion(); + if (!regionp) + { + continue; + } + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) + { + // 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); + continue; + } + if (inSpatialChannel()) { // add session for region or parcel voice. - LLViewerRegion *regionp = gAgent.getRegion(); if (!regionp || regionp->getRegionID().isNull()) { continue; @@ -2752,6 +2767,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result } else { + LL_WARNS("Voice") << "Invalid voice provision request result:" << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; return; -- cgit v1.2.3 From 9161d5b6e69172d9663fca85321d5d7e2c7e55fe Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 22 Feb 2024 19:43:17 -0800 Subject: Initialize versions --- indra/newview/llvoicevivox.cpp | 4 ++++ indra/newview/llvoicewebrtc.cpp | 3 +++ 2 files changed, 7 insertions(+) (limited to 'indra') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index d708870314..e003dc3317 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -360,6 +360,10 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.voiceServerType = VISIBLE_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); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0332557229..f276811b25 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -291,6 +291,9 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; + mVoiceVersion.minorVersion = 0; + mVoiceVersion.majorVersion = 2; + mVoiceVersion.mBuildVersion = ""; #if LL_DARWIN || LL_LINUX // HACK: THIS DOES NOT BELONG HERE -- cgit v1.2.3