Skip to content

Commit fffad2b

Browse files
authored
chore(llc): improved sfu stats (#1122)
* improved sfu stats * tweaks * tweaks * tweak * fix * tweak * fix
1 parent 8bba862 commit fffad2b

File tree

11 files changed

+201
-129
lines changed

11 files changed

+201
-129
lines changed

packages/stream_video/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* Fixed incoming call timeout handling.
55
- Use `streamVideo.observeCoreRingingEventsForBackground()` instead of `streamVideo.observeCallDeclinedRingingEvent()` in `firebaseMessagingBackgroundHandler` to support all necessary events.
66

7+
* Improved SFU stats implementation.
8+
79
## 1.0.1
810

911
### ✅ Added

packages/stream_video/lib/src/call/call.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,12 @@ class Call {
967967
networkMonitor: networkMonitor,
968968
streamVideo: _streamVideo,
969969
statsOptions: _sfuStatsOptions!,
970-
leftoverTraceRecords: _previousSession?.getTrace().snapshot ?? [],
970+
leftoverTraceRecords:
971+
_previousSession
972+
?.getTrace()
973+
.expand((slice) => slice.snapshot)
974+
.toList() ??
975+
const [],
971976
onReconnectionNeeded: (pc, strategy) {
972977
_session?.trace('pc_reconnection_needed', {
973978
'peerConnectionId': pc.type.name,
@@ -1481,8 +1486,8 @@ class Call {
14811486
return;
14821487
}
14831488

1484-
_session?.trace('call_reconnect', {
1485-
'strategy': _reconnectStrategy.name,
1489+
_session?.trace('callReconnect', {
1490+
'strategy': strategy.name,
14861491
});
14871492

14881493
_stateManager.lifecycleCallConnecting(
@@ -1531,8 +1536,8 @@ class Call {
15311536
await _reconnectMigrate();
15321537
}
15331538

1534-
_session?.trace('call_reconnect_success', {
1535-
'strategy': _reconnectStrategy.name,
1539+
_session?.trace('callReconnectSuccess', {
1540+
'strategy': strategy.name,
15361541
});
15371542
} catch (error) {
15381543
switch (error) {
@@ -1541,8 +1546,8 @@ class Call {
15411546
_logger.w(() => '[reconnect] unrecoverable error');
15421547
_stateManager.lifecycleCallReconnectingFailed();
15431548

1544-
_session?.trace('call_reconnect_failed', {
1545-
'strategy': _reconnectStrategy.name,
1549+
_session?.trace('callReconnectFailed', {
1550+
'strategy': strategy.name,
15461551
'error': error.toString(),
15471552
});
15481553

packages/stream_video/lib/src/call/session/call_session.dart

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class CallSession extends Disposable {
104104
final InternetConnection networkMonitor;
105105
final StatsOptions statsOptions;
106106
final Tracer _tracer;
107+
final Tracer _zonedTracer = Tracer(null);
107108
final StreamVideo _streamVideo;
108109

109110
final Duration joinResponseTimeout;
@@ -121,6 +122,7 @@ class CallSession extends Disposable {
121122
StatsReporter? statsReporter;
122123

123124
Timer? _peerConnectionCheckTimer;
125+
bool _isLeavingOrClosed = false;
124126

125127
sfu_models.ClientDetails? _clientDetails;
126128

@@ -132,12 +134,13 @@ class CallSession extends Disposable {
132134
onCancel: () => Result.error('UpdateViewportVisibility cancelled'),
133135
);
134136

135-
TraceSlice getTrace() {
136-
return _tracer.take();
137+
List<TraceSlice> getTrace() {
138+
return [_tracer.take(), _zonedTracer.take()];
137139
}
138140

139141
void setTraceEnabled(bool enabled) {
140142
_tracer.setEnabled(enabled);
143+
_zonedTracer.setEnabled(enabled);
141144
}
142145

143146
void trace(String tag, dynamic data) {
@@ -459,6 +462,12 @@ class CallSession extends Disposable {
459462
fastReconnectDeadline: event.fastReconnectDeadline,
460463
),
461464
);
465+
} on TimeoutException catch (e, stk) {
466+
final message =
467+
'Waiting for "joinResponse" has timed out after ${joinResponseTimeout.inMilliseconds}ms';
468+
_tracer.trace('joinRequestTimeout', message);
469+
_logger.e(() => '[start] failed: $e');
470+
return Result.failure(VideoErrors.compose(e, stk));
462471
} catch (e, stk) {
463472
_logger.e(() => '[start] failed: $e');
464473
return Result.failure(VideoErrors.compose(e, stk));
@@ -585,6 +594,7 @@ class CallSession extends Disposable {
585594

586595
void leave({String? reason}) {
587596
_logger.d(() => '[leave] no args');
597+
_isLeavingOrClosed = true;
588598
sfuWS.leave(sessionId: sessionId, reason: reason);
589599
}
590600

@@ -593,6 +603,7 @@ class CallSession extends Disposable {
593603
String? closeReason,
594604
}) async {
595605
_logger.d(() => '[close] code: $code, closeReason: $closeReason');
606+
_isLeavingOrClosed = true;
596607

597608
await _eventsSubscription?.cancel();
598609
await _networkStatusSubscription?.cancel();
@@ -614,6 +625,7 @@ class CallSession extends Disposable {
614625
@override
615626
Future<void> dispose() async {
616627
_logger.d(() => '[dispose] no args');
628+
_isLeavingOrClosed = true;
617629

618630
await close(StreamWebSocketCloseCode.normalClosure);
619631
return await super.dispose();
@@ -683,6 +695,7 @@ class CallSession extends Disposable {
683695
} else if (event is SfuParticipantLeftEvent) {
684696
stateManager.sfuParticipantLeft(event);
685697
} else if (event is SfuConnectionQualityChangedEvent) {
698+
_tracer.trace('ConnectionQualityChanged', event.toJson());
686699
stateManager.sfuConnectionQualityChanged(event);
687700
} else if (event is SfuAudioLevelChangedEvent) {
688701
stateManager.sfuUpdateAudioLevelChanged(event);
@@ -905,7 +918,7 @@ class CallSession extends Disposable {
905918
}
906919

907920
Future<void> _onRenegotiationNeeded(StreamPeerConnection pc) async {
908-
if (stateManager.callState.status.isDisconnected) {
921+
if (_isLeavingOrClosed || stateManager.callState.status.isDisconnected) {
909922
_logger.w(() => '[negotiate] call is disconnected');
910923
return;
911924
}
@@ -1037,7 +1050,7 @@ class CallSession extends Disposable {
10371050
}
10381051

10391052
final result = TracerZone.run(
1040-
_tracer,
1053+
_zonedTracer,
10411054
++zonedTracerSeq,
10421055
() async {
10431056
return rtcManager.setCameraEnabled(
@@ -1060,7 +1073,7 @@ class CallSession extends Disposable {
10601073
}
10611074

10621075
final result = TracerZone.run(
1063-
_tracer,
1076+
_zonedTracer,
10641077
++zonedTracerSeq,
10651078
() async {
10661079
return rtcManager.setMicrophoneEnabled(

packages/stream_video/lib/src/call/session/call_session_factory.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,17 @@ class CallSessionFactory {
7272

7373
_logger.v(() => '[makeCallSession] sfuName: $sfuName, sfuUrl: $sfuUrl');
7474

75-
final tracer = Tracer('$sessionSeq')
75+
final tracer = Tracer('$sessionSeq-$sfuName')
7676
..setEnabled(statsOptions.enableRtcStats)
7777
..traceMultiple(
7878
leftoverTraceRecords
7979
.map(
8080
(r) => r.copyWith(
81-
id: '${max(0, sessionSeq - 1)}',
81+
id: '${max(0, sessionSeq - 1)}-$sfuName',
8282
),
8383
)
8484
.toList(),
85-
)
86-
..trace('create', {'url': sfuName});
85+
);
8786

8887
return CallSession(
8988
sessionSeq: sessionSeq,

packages/stream_video/lib/src/call/stats/sfu_stats_reporter.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,14 @@ class SfuStatsReporter {
188188
final publisherTrace = callSession.rtcManager?.publisher?.tracer
189189
.take();
190190
final sessionTrace = callSession.getTrace();
191+
final mediaDeviceNotifierTrace = RtcMediaDeviceNotifier.instance
192+
.getTrace();
191193

192194
traces.addAll([
193195
if (subscriberTrace != null) subscriberTrace,
194196
if (publisherTrace != null) publisherTrace,
195-
sessionTrace,
197+
...sessionTrace,
198+
mediaDeviceNotifierTrace,
196199
]);
197200
}
198201

packages/stream_video/lib/src/call/stats/stats_reporter.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class StatsReporter extends StateNotifier<CallMetrics?> {
4646
return Stream.periodic(interval, (tick) => (collectStats(), tick)).asyncMap(
4747
(data) async {
4848
final stats = await data.$1;
49+
if (!mounted) return stats;
50+
4951
unawaited(_processStats(stats, data.$2));
5052
return stats;
5153
},
@@ -92,6 +94,8 @@ class StatsReporter extends StateNotifier<CallMetrics?> {
9294
stats,
9395
int tick,
9496
) async {
97+
if (!mounted) return;
98+
9599
var publisherStats = state?.publisher ?? PeerConnectionStats.empty();
96100
var subscriberStats = state?.subscriber ?? PeerConnectionStats.empty();
97101

0 commit comments

Comments
 (0)