@@ -20,7 +20,11 @@ class MeshBackend extends CallBackend {
20
20
/// participant:volume
21
21
final Map <CallParticipant , double > _audioLevelsMap = {};
22
22
23
- StreamSubscription <CallSession >? _callSubscription;
23
+ /// The stream is used to prepare for incoming peer calls like registering listeners
24
+ StreamSubscription <CallSession >? _callSetupSubscription;
25
+
26
+ /// The stream is used to signal the start of an incoming peer call
27
+ StreamSubscription <CallSession >? _callStartSubscription;
24
28
25
29
Timer ? _activeSpeakerLoopTimeout;
26
30
@@ -109,14 +113,32 @@ class MeshBackend extends CallBackend {
109
113
);
110
114
}
111
115
116
+ /// Register listeners for a peer call to use for the group calls, that is
117
+ /// needed before even call is added to `_callSessions` .
118
+ /// We do this here for onStreamAdd and onStreamRemoved to make sure we don't
119
+ /// miss any events that happen before the call is completely started.
120
+ void _registerListenersBeforeCallAdd (CallSession call) {
121
+ call.onStreamAdd.stream.listen ((stream) {
122
+ if (! stream.isLocal ()) {
123
+ onStreamAdd.add (stream);
124
+ }
125
+ });
126
+
127
+ call.onStreamRemoved.stream.listen ((stream) {
128
+ if (! stream.isLocal ()) {
129
+ onStreamRemoved.add (stream);
130
+ }
131
+ });
132
+ }
133
+
112
134
Future <void > _addCall (GroupCallSession groupCall, CallSession call) async {
113
135
_callSessions.add (call);
114
- await _initCall (groupCall, call);
136
+ _initCall (groupCall, call);
115
137
groupCall.onGroupCallEvent.add (GroupCallStateChange .callsChanged);
116
138
}
117
139
118
140
/// init a peer call from group calls.
119
- Future < void > _initCall (GroupCallSession groupCall, CallSession call) async {
141
+ void _initCall (GroupCallSession groupCall, CallSession call) {
120
142
if (call.remoteUserId == null ) {
121
143
throw MatrixSDKVoipException (
122
144
'Cannot init call without proper invitee user and device Id' ,
@@ -141,18 +163,6 @@ class MeshBackend extends CallBackend {
141
163
call.onCallHangupNotifierForGroupCalls.stream.listen ((event) async {
142
164
await _onCallHangup (groupCall, call);
143
165
});
144
-
145
- call.onStreamAdd.stream.listen ((stream) {
146
- if (! stream.isLocal ()) {
147
- onStreamAdd.add (stream);
148
- }
149
- });
150
-
151
- call.onStreamRemoved.stream.listen ((stream) {
152
- if (! stream.isLocal ()) {
153
- onStreamRemoved.add (stream);
154
- }
155
- });
156
166
}
157
167
158
168
Future <void > _replaceCall (
@@ -171,7 +181,8 @@ class MeshBackend extends CallBackend {
171
181
_callSessions.add (replacementCall);
172
182
173
183
await _disposeCall (groupCall, existingCall, CallErrorCode .replaced);
174
- await _initCall (groupCall, replacementCall);
184
+ _registerListenersBeforeCallAdd (replacementCall);
185
+ _initCall (groupCall, replacementCall);
175
186
176
187
groupCall.onGroupCallEvent.add (GroupCallStateChange .callsChanged);
177
188
}
@@ -657,7 +668,49 @@ class MeshBackend extends CallBackend {
657
668
return ;
658
669
}
659
670
660
- Future <void > _onIncomingCall (
671
+ void _onIncomingCallInMeshSetup (
672
+ GroupCallSession groupCall,
673
+ CallSession newCall,
674
+ ) {
675
+ // The incoming calls may be for another room, which we will ignore.
676
+ if (newCall.room.id != groupCall.room.id) return ;
677
+
678
+ if (newCall.state != CallState .kRinging) {
679
+ Logs ().v (
680
+ '[_onIncomingCallInMeshSetup] Incoming call no longer in ringing state. Ignoring.' ,
681
+ );
682
+ return ;
683
+ }
684
+
685
+ if (newCall.groupCallId == null ||
686
+ newCall.groupCallId != groupCall.groupCallId) {
687
+ Logs ().v (
688
+ '[_onIncomingCallInMeshSetup] Incoming call with groupCallId ${newCall .groupCallId } ignored because it doesn\' t match the current group call' ,
689
+ );
690
+ return ;
691
+ }
692
+
693
+ final existingCall = _getCallForParticipant (
694
+ groupCall,
695
+ CallParticipant (
696
+ groupCall.voip,
697
+ userId: newCall.remoteUserId! ,
698
+ deviceId: newCall.remoteDeviceId,
699
+ ),
700
+ );
701
+
702
+ // if it's an existing call, `_registerListenersForCall` will be called in
703
+ // `_replaceCall` that is used in `_onIncomingCallStart`.
704
+ if (existingCall != null ) return ;
705
+
706
+ Logs ().v (
707
+ '[_onIncomingCallInMeshSetup] GroupCallSession: incoming call from: ${newCall .remoteUserId }${newCall .remoteDeviceId }${newCall .remotePartyId }' ,
708
+ );
709
+
710
+ _registerListenersBeforeCallAdd (newCall);
711
+ }
712
+
713
+ Future <void > _onIncomingCallInMeshStart (
661
714
GroupCallSession groupCall,
662
715
CallSession newCall,
663
716
) async {
@@ -667,14 +720,16 @@ class MeshBackend extends CallBackend {
667
720
}
668
721
669
722
if (newCall.state != CallState .kRinging) {
670
- Logs ().w ('Incoming call no longer in ringing state. Ignoring.' );
723
+ Logs ().v (
724
+ '[_onIncomingCallInMeshStart] Incoming call no longer in ringing state. Ignoring.' ,
725
+ );
671
726
return ;
672
727
}
673
728
674
729
if (newCall.groupCallId == null ||
675
730
newCall.groupCallId != groupCall.groupCallId) {
676
731
Logs ().v (
677
- 'Incoming call with groupCallId ${newCall .groupCallId } ignored because it doesn\' t match the current group call' ,
732
+ '[_onIncomingCallInMeshStart] Incoming call with groupCallId ${newCall .groupCallId } ignored because it doesn\' t match the current group call' ,
678
733
);
679
734
await newCall.reject ();
680
735
return ;
@@ -694,7 +749,7 @@ class MeshBackend extends CallBackend {
694
749
}
695
750
696
751
Logs ().v (
697
- 'GroupCallSession: incoming call from: ${newCall .remoteUserId }${newCall .remoteDeviceId }${newCall .remotePartyId }' ,
752
+ '[_onIncomingCallInMeshStart] GroupCallSession: incoming call from: ${newCall .remoteUserId }${newCall .remoteDeviceId }${newCall .remotePartyId }' ,
698
753
);
699
754
700
755
// Check if the user calling has an existing call and use this call instead.
@@ -800,7 +855,8 @@ class MeshBackend extends CallBackend {
800
855
801
856
_activeSpeaker = null ;
802
857
_activeSpeakerLoopTimeout? .cancel ();
803
- await _callSubscription? .cancel ();
858
+ await _callSetupSubscription? .cancel ();
859
+ await _callStartSubscription? .cancel ();
804
860
}
805
861
806
862
@override
@@ -826,11 +882,16 @@ class MeshBackend extends CallBackend {
826
882
GroupCallSession groupCall,
827
883
) async {
828
884
for (final call in _callSessions) {
829
- await _onIncomingCall (groupCall, call);
885
+ _onIncomingCallInMeshSetup (groupCall, call);
886
+ await _onIncomingCallInMeshStart (groupCall, call);
830
887
}
831
888
832
- _callSubscription = groupCall.voip.onIncomingCall.stream.listen (
833
- (newCall) => _onIncomingCall (groupCall, newCall),
889
+ _callSetupSubscription = groupCall.voip.onIncomingCallSetup.stream.listen (
890
+ (newCall) => _onIncomingCallInMeshSetup (groupCall, newCall),
891
+ );
892
+
893
+ _callStartSubscription = groupCall.voip.onIncomingCallStart.stream.listen (
894
+ (newCall) => _onIncomingCallInMeshStart (groupCall, newCall),
834
895
);
835
896
836
897
_onActiveSpeakerLoop (groupCall);
@@ -883,6 +944,8 @@ class MeshBackend extends CallBackend {
883
944
// party id set to when answered
884
945
newCall.remoteSessionId = mem.membershipId;
885
946
947
+ _registerListenersBeforeCallAdd (newCall);
948
+
886
949
await newCall.placeCallWithStreams (
887
950
_getLocalStreams (),
888
951
requestScreenSharing: mem.feeds? .any (
0 commit comments