summaryrefslogtreecommitdiff
path: root/indra/newview/llvoicewebrtc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llvoicewebrtc.cpp')
-rw-r--r--indra/newview/llvoicewebrtc.cpp211
1 files changed, 195 insertions, 16 deletions
diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp
index 06ccd23670..c6967a8743 100644
--- a/indra/newview/llvoicewebrtc.cpp
+++ b/indra/newview/llvoicewebrtc.cpp
@@ -61,10 +61,12 @@
#include "llrand.h"
#include "llviewerwindow.h"
#include "llviewercamera.h"
+#include "llviewerstats.h"
#include "llversioninfo.h"
#include "llviewernetwork.h"
#include "llnotificationsutil.h"
+#include "llnearbyvoicemoderation.h"
#include "llcorehttputil.h"
#include "lleventfilter.h"
@@ -80,6 +82,8 @@
const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc";
+const F32 STATS_TIMER_DELAY = 2.0;
+
namespace {
const F32 MAX_AUDIO_DIST = 50.0f;
@@ -2496,17 +2500,18 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection)
// callback from llwebrtc
void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp)
{
+ connectionPtr_t connection = shared_from_this();
LL::WorkQueue::postMaybe(mMainQueue,
- [=, this] {
- if (mShutDown)
+ [=] {
+ if (connection->mShutDown)
{
return;
}
LL_DEBUGS("Voice") << "On Offer Available." << LL_ENDL;
- mChannelSDP = sdp;
- if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START)
+ connection->mChannelSDP = sdp;
+ if (connection->mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START)
{
- mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION;
+ connection->mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION;
}
});
}
@@ -2523,16 +2528,17 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp)
// callback from llwebrtc
void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface)
{
+ connectionPtr_t connection = shared_from_this();
LL::WorkQueue::postMaybe(mMainQueue,
- [=, this] {
- if (mShutDown)
+ [=] {
+ if (connection->mShutDown)
{
return;
}
LL_DEBUGS("Voice") << "On AudioEstablished." << LL_ENDL;
- mWebRTCAudioInterface = audio_interface;
- mWebRTCAudioInterface->setMute(true); // mute will be set appropriately later when we finish setting up.
- setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED);
+ connection->mWebRTCAudioInterface = audio_interface;
+ connection->mWebRTCAudioInterface->setMute(true); // mute will be set appropriately later when we finish setting up.
+ connection->setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED);
});
}
@@ -2902,6 +2908,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()
}
mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume);
LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID);
+ resetConnectionStats();
setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL);
break;
}
@@ -2955,6 +2962,13 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()
sendJoin();
}
}
+
+ static LLTimer stats_timer;
+ if (stats_timer.getElapsedTimeF32() > STATS_TIMER_DELAY)
+ {
+ mWebRTCPeerConnectionInterface->gatherConnectionStats();
+ stats_timer.reset();
+ }
}
break;
}
@@ -3086,6 +3100,13 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b
LL_WARNS("Voice") << "Expected object from data channel:" << data << LL_ENDL;
return;
}
+
+ bool is_primary_region = mPrimary;
+ if (!mPrimary && isSpatial() && gAgent.getRegion())
+ {
+ is_primary_region = (mRegionID == gAgent.getRegion()->getRegionID());
+ LL_WARNS() << "mPrimary is false, expected: " << is_primary_region << " connection state: " << getVoiceConnectionState() << LL_ENDL;
+ }
boost::json::object voice_data = voice_data_parsed.as_object();
boost::json::object mute;
boost::json::object user_gain;
@@ -3171,12 +3192,63 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b
if (participant_obj.contains("m") && participant_obj["m"].is_bool())
{
- participant->mIsModeratorMuted = participant_obj["m"].as_bool();
+ bool is_moderator_muted = participant_obj["m"].as_bool();
+ if (isSpatial())
+ {
+ // ignore muted flags from non-primary server
+ if (is_primary_region || primary)
+ {
+ participant->mIsModeratorMuted = is_moderator_muted;
+ if (gAgentID == agent_id)
+ {
+ LLNearbyVoiceModeration::getInstance()->setMutedInfo(mChannelID, is_moderator_muted);
+ }
+ }
+ }
+ else
+ {
+ participant->mIsModeratorMuted = is_moderator_muted;
+ }
}
}
}
- }
+ else
+ {
+ if (isSpatial() && (is_primary_region || primary))
+ {
+ // mute info message can be received before join message, so try to mute again later
+ if (participant_obj.contains("m") && participant_obj["m"].is_bool())
+ {
+ LL_WARNS() << "Mute info msg received: " << participant_obj["m"].as_bool()
+ << " but participant " << agent_id
+ << " was not found in channel " << mChannelID << LL_ENDL;
+ bool is_moderator_muted = participant_obj["m"].as_bool();
+ std::string channel_id = mChannelID;
+ F32 delay { 1.5f };
+ doAfterInterval(
+ [channel_id, agent_id, is_moderator_muted]()
+ {
+ LLWebRTCVoiceClient::participantStatePtr_t participant =
+ LLWebRTCVoiceClient::getInstance()->findParticipantByID(channel_id, agent_id);
+ if (participant)
+ {
+ participant->mIsModeratorMuted = is_moderator_muted;
+ LL_WARNS() << "Participant " << agent_id << " is found after delay, is_muted: " << is_moderator_muted << LL_ENDL;
+ if (gAgentID == agent_id)
+ {
+ LLNearbyVoiceModeration::getInstance()->setMutedInfo(channel_id, is_moderator_muted);
+ }
+ }
+ else
+ {
+ LL_WARNS() << "Participant " << agent_id << " is still not found in channel " << channel_id << LL_ENDL;
+ }
+ }, delay);
+ }
+ }
+ }
+ }
// tell the simulator to set the mute and volume data for this
// participant, if there are any updates.
boost::json::object root;
@@ -3207,17 +3279,18 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b
// llwebrtc callback
void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface)
{
+ connectionPtr_t connection = shared_from_this();
LL::WorkQueue::postMaybe(mMainQueue,
- [=, this] {
- if (mShutDown)
+ [=] {
+ if (connection->mShutDown)
{
return;
}
if (data_interface)
{
- mWebRTCDataInterface = data_interface;
- mWebRTCDataInterface->setDataObserver(this);
+ connection->mWebRTCDataInterface = data_interface;
+ connection->mWebRTCDataInterface->setDataObserver(connection.get());
}
});
}
@@ -3247,6 +3320,112 @@ void LLVoiceWebRTCConnection::sendJoin()
mWebRTCDataInterface->sendData(json_data, false);
}
+void LLVoiceWebRTCConnection::OnStatsDelivered(const llwebrtc::LLWebRTCStatsMap& stats_data)
+{
+ LL::WorkQueue::postMaybe(mMainQueue, [=, this]
+ {
+ if (mShutDown)
+ {
+ return;
+ }
+ for (const auto& [stats_id, attributes] : stats_data)
+ {
+ if (attributes.contains("currentRoundTripTime"))
+ {
+ F32 rtt_seconds = 0.0f;
+ LLStringUtil::convertToF32(attributes.at("currentRoundTripTime"), rtt_seconds);
+ sample(LLStatViewer::WEBRTC_LATENCY, rtt_seconds * 1000.0f);
+ }
+ if (attributes.contains("availableOutgoingBitrate"))
+ {
+ F32 bitrate_bps = 0.0f;
+ LLStringUtil::convertToF32(attributes.at("availableOutgoingBitrate"), bitrate_bps);
+ sample(LLStatViewer::WEBRTC_UPLOAD_BANDWIDTH, bitrate_bps / 1000.0f);
+ }
+
+ // Stat type detection below is heuristic-based.
+ // It's relied on specific fields to distinguish outbound-rtp, remote-inbound-rtp, and inbound-rtp.
+ // This approach works with current WebRTC stats but may need updating later.
+
+ // Outbound RTP
+ if (attributes.contains("mediaSourceId"))
+ {
+ U32 out_packets_sent = 0;
+ LLStringUtil::convertToU32(attributes.at("packetsSent"), out_packets_sent);
+ sample(LLStatViewer::WEBRTC_PACKETS_OUT_SENT, out_packets_sent);
+ }
+ // Remote-Inbound RTP
+ else if (attributes.contains("localId"))
+ {
+ if (attributes.contains("packetsLost"))
+ {
+ U32 out_packets_lost = 0;
+ LLStringUtil::convertToU32(attributes.at("packetsLost"), out_packets_lost);
+ sample(LLStatViewer::WEBRTC_PACKETS_OUT_LOST, out_packets_lost);
+ }
+ if (attributes.contains("jitter"))
+ {
+ F32 jitter_seconds = 0.0f;
+ LLStringUtil::convertToF32(attributes.at("jitter"), jitter_seconds);
+ sample(LLStatViewer::WEBRTC_JITTER_OUT, jitter_seconds * 1000.0f);
+ }
+ }
+ // Inbound RTP
+ else if (attributes.contains("jitterBufferDelay"))
+ {
+ if (attributes.contains("packetsLost"))
+ {
+ U32 in_packets_lost = 0;
+ LLStringUtil::convertToU32(attributes.at("packetsLost"), in_packets_lost);
+ sample(LLStatViewer::WEBRTC_PACKETS_IN_LOST, in_packets_lost);
+ }
+ if (attributes.contains("packetsReceived"))
+ {
+ U32 in_packets_recv = 0;
+ LLStringUtil::convertToU32(attributes.at("packetsReceived"), in_packets_recv);
+ sample(LLStatViewer::WEBRTC_PACKETS_IN_RECEIVED, in_packets_recv);
+ }
+ if (attributes.contains("jitter"))
+ {
+ F32 jitter_seconds = 0.0f;
+ LLStringUtil::convertToF32(attributes.at("jitter"), jitter_seconds);
+ sample(LLStatViewer::WEBRTC_JITTER_IN, jitter_seconds * 1000.0f);
+ }
+ if (attributes.contains("jitterBufferDelay") && attributes.contains("jitterBufferEmittedCount"))
+ {
+ F32 total_delay_seconds = 0.0f;
+ F32 emitted_count_f = 0.0f;
+
+ // total delay in seconds
+ LLStringUtil::convertToF32(attributes.at("jitterBufferDelay"), total_delay_seconds);
+
+ // number of packets played out
+ LLStringUtil::convertToF32(attributes.at("jitterBufferEmittedCount"), emitted_count_f);
+ if (emitted_count_f > 0.0f)
+ {
+ F32 avg_delay_seconds = total_delay_seconds / emitted_count_f;
+ F32 avg_delay_ms = avg_delay_seconds * 1000.0f;
+ sample(LLStatViewer::WEBRTC_JITTER_BUFFER, avg_delay_seconds * 1000.0f);
+ }
+ }
+ }
+ }
+ });
+}
+
+void LLVoiceWebRTCConnection::resetConnectionStats()
+{
+ sample(LLStatViewer::WEBRTC_JITTER_BUFFER, 0);
+ sample(LLStatViewer::WEBRTC_JITTER_IN, 0);
+ sample(LLStatViewer::WEBRTC_JITTER_OUT, 0);
+ sample(LLStatViewer::WEBRTC_LATENCY, 0);
+ sample(LLStatViewer::WEBRTC_PACKETS_IN_LOST, 0);
+ sample(LLStatViewer::WEBRTC_PACKETS_IN_RECEIVED, 0);
+ sample(LLStatViewer::WEBRTC_PACKETS_OUT_SENT, 0);
+ sample(LLStatViewer::WEBRTC_PACKETS_OUT_LOST, 0);
+ sample(LLStatViewer::WEBRTC_UPLOAD_BANDWIDTH, 0);
+}
+
/////////////////////////////
// WebRTC Spatial Connection