Skip to content

Commit 90f5ad3

Browse files
#5018 add webrtc connection statistics
1 parent e740bd2 commit 90f5ad3

File tree

9 files changed

+289
-0
lines changed

9 files changed

+289
-0
lines changed

indra/llwebrtc/llwebrtc.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,57 @@ void LLWebRTCPeerConnectionImpl::unsetDataObserver(LLWebRTCDataObserver* observe
15211521
}
15221522
}
15231523

1524+
class LLStatsCollectorCallback : public webrtc::RTCStatsCollectorCallback
1525+
{
1526+
public:
1527+
typedef std::function<void(const LLWebRTCStatsMap&)> StatsCallback;
1528+
1529+
LLStatsCollectorCallback(StatsCallback callback) : callback_(callback) {}
1530+
1531+
void OnStatsDelivered(const webrtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override
1532+
{
1533+
if (callback_)
1534+
{
1535+
// Transform RTCStatsReport stats to simple map
1536+
LLWebRTCStatsMap stats_map;
1537+
for (const auto& stats : *report)
1538+
{
1539+
std::map<std::string, std::string> stat_attributes;
1540+
1541+
// Convert each attribute to string format
1542+
for (const auto& attribute : stats.Attributes())
1543+
{
1544+
stat_attributes[attribute.name()] = attribute.ToString();
1545+
}
1546+
stats_map[stats.id()] = stat_attributes;
1547+
}
1548+
callback_(stats_map);
1549+
}
1550+
}
1551+
1552+
private:
1553+
StatsCallback callback_;
1554+
};
1555+
1556+
void LLWebRTCPeerConnectionImpl::gatherConnectionStats()
1557+
{
1558+
if (!mPeerConnection)
1559+
{
1560+
return;
1561+
}
1562+
1563+
auto stats_callback = webrtc::make_ref_counted<LLStatsCollectorCallback>(
1564+
[this](const LLWebRTCStatsMap& generic_stats)
1565+
{
1566+
for (auto& observer : mSignalingObserverList)
1567+
{
1568+
observer->OnStatsDelivered(generic_stats);
1569+
}
1570+
});
1571+
1572+
mPeerConnection->GetStats(stats_callback.get());
1573+
}
1574+
15241575
LLWebRTCImpl * gWebRTCImpl = nullptr;
15251576
LLWebRTCDeviceInterface * getDeviceInterface()
15261577
{

indra/llwebrtc/llwebrtc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#ifndef LLWEBRTC_H
3939
#define LLWEBRTC_H
4040

41+
#include <map>
4142
#include <string>
4243
#include <vector>
4344

@@ -55,6 +56,7 @@
5556

5657
namespace llwebrtc
5758
{
59+
typedef std::map<std::string, std::map<std::string, std::string>> LLWebRTCStatsMap;
5860

5961
class LLWebRTCLogCallback
6062
{
@@ -240,6 +242,8 @@ class LLWebRTCSignalingObserver
240242
// Called when the data channel has been established and data
241243
// transfer can begin.
242244
virtual void OnDataChannelReady(LLWebRTCDataInterface *data_interface) = 0;
245+
246+
virtual void OnStatsDelivered(const LLWebRTCStatsMap& stats_data) {}
243247
};
244248

245249
// LLWebRTCPeerConnectionInterface representsd a connection to a peer,
@@ -273,6 +277,8 @@ class LLWebRTCPeerConnectionInterface
273277
virtual void unsetSignalingObserver(LLWebRTCSignalingObserver* observer) = 0;
274278

275279
virtual void AnswerAvailable(const std::string &sdp) = 0;
280+
281+
virtual void gatherConnectionStats() = 0;
276282
};
277283

278284
// The following define the dynamic linked library

indra/llwebrtc/llwebrtc_impl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,8 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
648648
void enableSenderTracks(bool enable);
649649
void enableReceiverTracks(bool enable);
650650

651+
void gatherConnectionStats();
652+
651653
protected:
652654

653655
LLWebRTCImpl * mWebRTCImpl;

indra/newview/app_settings/settings.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6060,6 +6060,39 @@
60606060
<key>Value</key>
60616061
<integer>0</integer>
60626062
</map>
6063+
<key>OpenDebugStatVoice</key>
6064+
<map>
6065+
<key>Comment</key>
6066+
<string>Expand Voice (WebRTC) stats display</string>
6067+
<key>Persist</key>
6068+
<integer>1</integer>
6069+
<key>Type</key>
6070+
<string>Boolean</string>
6071+
<key>Value</key>
6072+
<integer>1</integer>
6073+
</map>
6074+
<key>OpenDebugStatVoiceOutgoing</key>
6075+
<map>
6076+
<key>Comment</key>
6077+
<string>Expand Outgoing audio (Voice) stats display</string>
6078+
<key>Persist</key>
6079+
<integer>1</integer>
6080+
<key>Type</key>
6081+
<string>Boolean</string>
6082+
<key>Value</key>
6083+
<integer>1</integer>
6084+
</map>
6085+
<key>OpenDebugStatVoiceIncoming</key>
6086+
<map>
6087+
<key>Comment</key>
6088+
<string>Expand Incoming audio (Voice) stats display</string>
6089+
<key>Persist</key>
6090+
<integer>1</integer>
6091+
<key>Type</key>
6092+
<string>Boolean</string>
6093+
<key>Value</key>
6094+
<integer>1</integer>
6095+
</map>
60636096
<key>OutBandwidth</key>
60646097
<map>
60656098
<key>Comment</key>

indra/newview/llviewerstats.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,20 @@ LLTrace::SampleStatHandle<LLUnit<F32, LLUnits::Percent> > HUDS_FRAME_PCT("huds_
263263
LLTrace::SampleStatHandle<LLUnit<F32, LLUnits::Percent> > UI_FRAME_PCT("ui_frame_pct");
264264
LLTrace::SampleStatHandle<LLUnit<F32, LLUnits::Percent> > SWAP_FRAME_PCT("swap_frame_pct");
265265
LLTrace::SampleStatHandle<LLUnit<F32, LLUnits::Percent> > IDLE_FRAME_PCT("idle_frame_pct");
266+
267+
268+
269+
LLTrace::SampleStatHandle<U32> WEBRTC_PACKETS_IN_LOST("webrtc_packets_in_lost", "Lost incoming packets"),
270+
WEBRTC_PACKETS_IN_RECEIVED("webrtc_packets_in_recv", "Incoming packets received"),
271+
WEBRTC_PACKETS_OUT_SENT("webrtc_packets_out_sent", "Outgoing packets sent"),
272+
WEBRTC_PACKETS_OUT_LOST("webrtc_packets_out_lost", "Lost outgoing packets");
273+
274+
LLTrace::SampleStatHandle<F32> WEBRTC_JITTER_OUT("webrtc_jitter_out", "Timing variation of outgoing audio"),
275+
WEBRTC_JITTER_IN("webrtc_jitter_in", "Timing variation of incoming audio"),
276+
WEBRTC_LATENCY("webrtc_latency", "Round-trip audio delay"),
277+
WEBRTC_UPLOAD_BANDWIDTH("webrtc_upload_bandwidth", "Estimated upload bandwidth"),
278+
WEBRTC_JITTER_BUFFER("webrtc_jitter_buffer", "Average delay added to smooth incoming audio");
279+
266280
}
267281

268282
LLViewerStats::LLViewerStats()

indra/newview/llviewerstats.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ extern LLTrace::EventStatHandle<F64Seconds > AVATAR_EDIT_TIME,
229229

230230
extern LLTrace::EventStatHandle<LLUnit<F32, LLUnits::Percent> > OBJECT_CACHE_HIT_RATE;
231231

232+
extern LLTrace::SampleStatHandle<U32> WEBRTC_PACKETS_IN_LOST, WEBRTC_PACKETS_IN_RECEIVED, WEBRTC_PACKETS_OUT_SENT, WEBRTC_PACKETS_OUT_LOST;
233+
extern LLTrace::SampleStatHandle<F32> WEBRTC_JITTER_OUT, WEBRTC_JITTER_IN, WEBRTC_LATENCY, WEBRTC_UPLOAD_BANDWIDTH, WEBRTC_JITTER_BUFFER;
232234
}
233235

234236
class LLViewerStats : public LLSingleton<LLViewerStats>

indra/newview/llvoicewebrtc.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "llrand.h"
6262
#include "llviewerwindow.h"
6363
#include "llviewercamera.h"
64+
#include "llviewerstats.h"
6465
#include "llversioninfo.h"
6566

6667
#include "llviewernetwork.h"
@@ -81,6 +82,8 @@
8182

8283
const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc";
8384

85+
const F32 STATS_TIMER_DELAY = 2.0;
86+
8487
namespace {
8588

8689
const F32 MAX_AUDIO_DIST = 50.0f;
@@ -2905,6 +2908,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()
29052908
}
29062909
mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume);
29072910
LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID);
2911+
resetConnectionStats();
29082912
setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL);
29092913
break;
29102914
}
@@ -2954,6 +2958,13 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()
29542958
sendJoin();
29552959
}
29562960
}
2961+
2962+
static LLTimer stats_timer;
2963+
if (stats_timer.getElapsedTimeF32() > STATS_TIMER_DELAY)
2964+
{
2965+
mWebRTCPeerConnectionInterface->gatherConnectionStats();
2966+
stats_timer.reset();
2967+
}
29572968
}
29582969
break;
29592970
}
@@ -3288,6 +3299,112 @@ void LLVoiceWebRTCConnection::sendJoin()
32883299
mWebRTCDataInterface->sendData(json_data, false);
32893300
}
32903301

3302+
void LLVoiceWebRTCConnection::OnStatsDelivered(const llwebrtc::LLWebRTCStatsMap& stats_data)
3303+
{
3304+
LL::WorkQueue::postMaybe(mMainQueue, [=, this]
3305+
{
3306+
if (mShutDown)
3307+
{
3308+
return;
3309+
}
3310+
for (const auto& [stats_id, attributes] : stats_data)
3311+
{
3312+
if (attributes.contains("currentRoundTripTime"))
3313+
{
3314+
F32 rtt_seconds = 0.0f;
3315+
LLStringUtil::convertToF32(attributes.at("currentRoundTripTime"), rtt_seconds);
3316+
sample(LLStatViewer::WEBRTC_LATENCY, rtt_seconds * 1000.0f);
3317+
}
3318+
if (attributes.contains("availableOutgoingBitrate"))
3319+
{
3320+
F32 bitrate_bps = 0.0f;
3321+
LLStringUtil::convertToF32(attributes.at("availableOutgoingBitrate"), bitrate_bps);
3322+
sample(LLStatViewer::WEBRTC_UPLOAD_BANDWIDTH, bitrate_bps / 1000.0f);
3323+
}
3324+
3325+
// Stat type detection below is heuristic-based.
3326+
// It's relied on specific fields to distinguish outbound-rtp, remote-inbound-rtp, and inbound-rtp.
3327+
// This approach works with current WebRTC stats but may need updating later.
3328+
3329+
// Outbound RTP
3330+
if (attributes.contains("mediaSourceId"))
3331+
{
3332+
U32 out_packets_sent = 0;
3333+
LLStringUtil::convertToU32(attributes.at("packetsSent"), out_packets_sent);
3334+
sample(LLStatViewer::WEBRTC_PACKETS_OUT_SENT, out_packets_sent);
3335+
}
3336+
// Remote-Inbound RTP
3337+
else if (attributes.contains("localId"))
3338+
{
3339+
if (attributes.contains("packetsLost"))
3340+
{
3341+
U32 out_packets_lost = 0;
3342+
LLStringUtil::convertToU32(attributes.at("packetsLost"), out_packets_lost);
3343+
sample(LLStatViewer::WEBRTC_PACKETS_OUT_LOST, out_packets_lost);
3344+
}
3345+
if (attributes.contains("jitter"))
3346+
{
3347+
F32 jitter_seconds = 0.0f;
3348+
LLStringUtil::convertToF32(attributes.at("jitter"), jitter_seconds);
3349+
sample(LLStatViewer::WEBRTC_JITTER_OUT, jitter_seconds * 1000.0f);
3350+
}
3351+
}
3352+
// Inbound RTP
3353+
else if (attributes.contains("jitterBufferDelay"))
3354+
{
3355+
if (attributes.contains("packetsLost"))
3356+
{
3357+
U32 in_packets_lost = 0;
3358+
LLStringUtil::convertToU32(attributes.at("packetsLost"), in_packets_lost);
3359+
sample(LLStatViewer::WEBRTC_PACKETS_IN_LOST, in_packets_lost);
3360+
}
3361+
if (attributes.contains("packetsReceived"))
3362+
{
3363+
U32 in_packets_recv = 0;
3364+
LLStringUtil::convertToU32(attributes.at("packetsReceived"), in_packets_recv);
3365+
sample(LLStatViewer::WEBRTC_PACKETS_IN_RECEIVED, in_packets_recv);
3366+
}
3367+
if (attributes.contains("jitter"))
3368+
{
3369+
F32 jitter_seconds = 0.0f;
3370+
LLStringUtil::convertToF32(attributes.at("jitter"), jitter_seconds);
3371+
sample(LLStatViewer::WEBRTC_JITTER_IN, jitter_seconds * 1000.0f);
3372+
}
3373+
if (attributes.contains("jitterBufferDelay") && attributes.contains("jitterBufferEmittedCount"))
3374+
{
3375+
F32 total_delay_seconds = 0.0f;
3376+
F32 emitted_count_f = 0.0f;
3377+
3378+
// total delay in seconds
3379+
LLStringUtil::convertToF32(attributes.at("jitterBufferDelay"), total_delay_seconds);
3380+
3381+
// number of packets played out
3382+
LLStringUtil::convertToF32(attributes.at("jitterBufferEmittedCount"), emitted_count_f);
3383+
if (emitted_count_f > 0.0f)
3384+
{
3385+
F32 avg_delay_seconds = total_delay_seconds / emitted_count_f;
3386+
F32 avg_delay_ms = avg_delay_seconds * 1000.0f;
3387+
sample(LLStatViewer::WEBRTC_JITTER_BUFFER, avg_delay_seconds * 1000.0f);
3388+
}
3389+
}
3390+
}
3391+
}
3392+
});
3393+
}
3394+
3395+
void LLVoiceWebRTCConnection::resetConnectionStats()
3396+
{
3397+
sample(LLStatViewer::WEBRTC_JITTER_BUFFER, 0);
3398+
sample(LLStatViewer::WEBRTC_JITTER_IN, 0);
3399+
sample(LLStatViewer::WEBRTC_JITTER_OUT, 0);
3400+
sample(LLStatViewer::WEBRTC_LATENCY, 0);
3401+
sample(LLStatViewer::WEBRTC_PACKETS_IN_LOST, 0);
3402+
sample(LLStatViewer::WEBRTC_PACKETS_IN_RECEIVED, 0);
3403+
sample(LLStatViewer::WEBRTC_PACKETS_OUT_SENT, 0);
3404+
sample(LLStatViewer::WEBRTC_PACKETS_OUT_LOST, 0);
3405+
sample(LLStatViewer::WEBRTC_UPLOAD_BANDWIDTH, 0);
3406+
}
3407+
32913408
/////////////////////////////
32923409
// WebRTC Spatial Connection
32933410

indra/newview/llvoicewebrtc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,8 @@ class LLWebRTCVoiceClient : public LLSingleton<LLWebRTCVoiceClient>,
540540
static bool sShuttingDown;
541541

542542
LLEventMailDrop mWebRTCPump;
543+
544+
LLSD mLastWebRTCStats;
543545
};
544546

545547

@@ -603,6 +605,8 @@ class LLVoiceWebRTCConnection :
603605
//@{
604606
void OnDataReceived(const std::string &data, bool binary) override;
605607
void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override;
608+
609+
void OnStatsDelivered(const llwebrtc::LLWebRTCStatsMap& stats_data) override;
606610
//@}
607611

608612
void OnDataReceivedImpl(const std::string &data, bool binary);
@@ -638,6 +642,8 @@ class LLVoiceWebRTCConnection :
638642

639643
void OnVoiceConnectionRequestSuccess(const LLSD &body);
640644

645+
void resetConnectionStats();
646+
641647
protected:
642648
typedef enum e_voice_connection_state
643649
{

0 commit comments

Comments
 (0)