Skip to content

Commit 5fbad7a

Browse files
renefloorBrazol
andauthored
feat: Add audioLevels and activeSpeakers (#882)
* feat: Add audioLevels and activeSpeakers * doc improvement Co-authored-by: Maciej Brażewicz <[email protected]> * Move logic to separate copyWith method --------- Co-authored-by: Maciej Brażewicz <[email protected]>
1 parent 0f90662 commit 5fbad7a

File tree

5 files changed

+57
-16
lines changed

5 files changed

+57
-16
lines changed

packages/stream_video/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## unreleased
2+
3+
✅ Added
4+
* Added `CallParticipantState.audioLevels` with the 10 last audio levels of the participant.
5+
* Added `CallState.activeSpeakers` to get list of currently active speakers.
6+
17
## 0.8.2
28

39
✅ Added

packages/stream_video/lib/src/call/state/mixins/state_sfu_mixin.dart

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ mixin StateSfuMixin on StateNotifier<CallState> {
106106
level.sessionId == participant.sessionId;
107107
});
108108
if (levelInfo != null) {
109-
return participant.copyWith(
109+
return participant.copyWithUpdatedAudioLevels(
110110
audioLevel: levelInfo.level,
111111
isSpeaking: levelInfo.isSpeaking,
112112
);
@@ -207,17 +207,18 @@ mixin StateSfuMixin on StateNotifier<CallState> {
207207
final participants = state.callParticipants.map((it) {
208208
if (it.userId == participant.userId &&
209209
it.sessionId == participant.sessionId) {
210-
return it.copyWith(
211-
name: participant.userName,
212-
custom: participant.custom,
213-
image: participant.userImage,
214-
trackIdPrefix: participant.trackLookupPrefix,
215-
audioLevel: participant.audioLevel,
216-
isSpeaking: participant.isSpeaking,
217-
isDominantSpeaker: participant.isDominantSpeaker,
218-
connectionQuality: participant.connectionQuality,
219-
roles: participant.roles,
220-
);
210+
return it
211+
.copyWith(
212+
name: participant.userName,
213+
custom: participant.custom,
214+
image: participant.userImage,
215+
trackIdPrefix: participant.trackLookupPrefix,
216+
isSpeaking: participant.isSpeaking,
217+
isDominantSpeaker: participant.isDominantSpeaker,
218+
connectionQuality: participant.connectionQuality,
219+
roles: participant.roles,
220+
)
221+
.copyWithUpdatedAudioLevels(audioLevel: participant.audioLevel);
221222
} else {
222223
return it;
223224
}

packages/stream_video/lib/src/call_state.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ class CallState extends Equatable {
141141
return callParticipants.where((element) => !element.isLocal).toList();
142142
}
143143

144+
List<CallParticipantState> get activeSpeakers {
145+
return callParticipants.where((element) => element.isSpeaking).toList();
146+
}
147+
144148
/// Returns a copy of this [CallState] with the given fields replaced
145149
/// with the new values.
146150
CallState copyWith({

packages/stream_video/lib/src/models/call_participant_state.dart

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import 'viewport_visibility.dart';
1414
class CallParticipantState
1515
with EquatableMixin
1616
implements Comparable<CallParticipantState> {
17-
const CallParticipantState({
17+
CallParticipantState({
1818
required this.userId,
1919
required this.roles,
2020
required this.name,
@@ -27,12 +27,13 @@ class CallParticipantState
2727
this.connectionQuality = SfuConnectionQuality.unspecified,
2828
this.isOnline = false,
2929
this.audioLevel = 0,
30+
List<double>? audioLevels,
3031
this.isSpeaking = false,
3132
this.isDominantSpeaker = false,
3233
this.isPinned = false,
3334
this.reaction,
3435
this.viewportVisibility = ViewportVisibility.unknown,
35-
});
36+
}) : audioLevels = audioLevels ?? [audioLevel];
3637

3738
final String userId;
3839
final List<String> roles;
@@ -45,7 +46,13 @@ class CallParticipantState
4546
final bool isLocal;
4647
final SfuConnectionQuality connectionQuality;
4748
final bool isOnline;
49+
50+
/// The latest audio level for the user.
4851
final double audioLevel;
52+
53+
/// List of the last 10 audio levels.
54+
final List<double> audioLevels;
55+
4956
final bool isSpeaking;
5057
final bool isDominantSpeaker;
5158
final bool isPinned;
@@ -54,6 +61,8 @@ class CallParticipantState
5461

5562
/// Returns a copy of this [CallParticipantState] with the given fields
5663
/// replaced with the new values.
64+
///
65+
/// If you want to update the audioLevel, consider using [copyWithUpdatedAudioLevels].
5766
CallParticipantState copyWith({
5867
String? userId,
5968
List<String>? roles,
@@ -67,6 +76,7 @@ class CallParticipantState
6776
SfuConnectionQuality? connectionQuality,
6877
bool? isOnline,
6978
double? audioLevel,
79+
List<double>? audioLevels,
7080
bool? isSpeaking,
7181
bool? isDominantSpeaker,
7282
bool? isPinned,
@@ -86,6 +96,7 @@ class CallParticipantState
8696
connectionQuality: connectionQuality ?? this.connectionQuality,
8797
isOnline: isOnline ?? this.isOnline,
8898
audioLevel: audioLevel ?? this.audioLevel,
99+
audioLevels: audioLevels ?? this.audioLevels,
89100
isSpeaking: isSpeaking ?? this.isSpeaking,
90101
isDominantSpeaker: isDominantSpeaker ?? this.isDominantSpeaker,
91102
isPinned: isPinned ?? this.isPinned,
@@ -94,6 +105,24 @@ class CallParticipantState
94105
);
95106
}
96107

108+
/// Copies the current state and adds the latest [audioLevel] to the last 10 [audioLevels].
109+
CallParticipantState copyWithUpdatedAudioLevels({
110+
required double audioLevel,
111+
bool? isSpeaking,
112+
}) {
113+
final levels = audioLevels;
114+
levels.add(audioLevel);
115+
while (levels.length > 10) {
116+
levels.removeAt(0);
117+
}
118+
119+
return copyWith(
120+
audioLevel: audioLevel,
121+
audioLevels: audioLevels,
122+
isSpeaking: isSpeaking,
123+
);
124+
}
125+
97126
/// Compares two participants.
98127
///
99128
/// The comparison is based on the [CallParticipantSortingPresets.regular].
@@ -119,7 +148,7 @@ class CallParticipantState
119148
'publishedTracks: $publishedTracks, '
120149
'isLocal: $isLocal, '
121150
'connectionQuality: $connectionQuality, isOnline: $isOnline, '
122-
'audioLevel: $audioLevel, isSpeaking: $isSpeaking, '
151+
'audioLevel: $audioLevel, audioLevels: $audioLevels, isSpeaking: $isSpeaking, '
123152
'isDominantSpeaker: $isDominantSpeaker, isPinned: $isPinned, '
124153
'reaction: $reaction, viewportVisibility: $viewportVisibility}';
125154
}
@@ -138,6 +167,7 @@ class CallParticipantState
138167
connectionQuality,
139168
isOnline,
140169
audioLevel,
170+
audioLevels,
141171
isSpeaking,
142172
isDominantSpeaker,
143173
isPinned,

packages/stream_video/test/call_participant_state_sorting_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ void main() {
5151
),
5252

5353
// Muted
54-
const CallParticipantState(
54+
CallParticipantState(
5555
name: 'C',
5656
userId: '3',
5757
sessionId: '3',

0 commit comments

Comments
 (0)