From 2d0fd9c393d28434f1820948e29b10d2f1d63a08 Mon Sep 17 00:00:00 2001 From: td Date: Fri, 24 Feb 2023 22:17:27 +0530 Subject: [PATCH 1/2] fix: make group call stuff async, let clients await what they need --- lib/src/voip/call.dart | 51 +++++---- lib/src/voip/group_call.dart | 157 +++++++++++++------------- lib/src/voip/utils.dart | 19 +++- lib/src/voip/voip.dart | 50 ++++---- lib/src/voip/voip_room_extension.dart | 55 ++++----- 5 files changed, 176 insertions(+), 156 deletions(-) diff --git a/lib/src/voip/call.dart b/lib/src/voip/call.dart index 621a9b46..6d6fc1fc 100644 --- a/lib/src/voip/call.dart +++ b/lib/src/voip/call.dart @@ -500,10 +500,10 @@ class CallSession { }); } - void answerWithStreams(List callFeeds) { + Future answerWithStreams(List callFeeds) async { if (inviteOrAnswerSent) return; Logs().d('nswering call $callId'); - gotCallFeedsForAnswer(callFeeds); + await gotCallFeedsForAnswer(callFeeds); } void replacedBy(CallSession newCall) { @@ -768,13 +768,14 @@ class CallSession { if (stream == null) { return false; } - stream.getTracks().forEach((track) { + for (final track in stream.getTracks()) { // screen sharing should only have 1 video track anyway, so this only // fires once - track.onEnded = () { - setScreensharingEnabled(false); + track.onEnded = () async { + await setScreensharingEnabled(false); }; - }); + } + await addLocalStream(stream, SDPStreamMetadataPurpose.Screenshare); return true; } catch (err) { @@ -1068,7 +1069,7 @@ class CallSession { return; } // stop play ringtone - voip.delegate.stopRingtone(); + await voip.delegate.stopRingtone(); if (direction == CallDirection.kIncoming) { setCallState(CallState.kCreateAnswer); @@ -1118,7 +1119,7 @@ class CallSession { /// Future reject({String? reason, bool shouldEmit = true}) async { // stop play ringtone - voip.delegate.stopRingtone(); + await voip.delegate.stopRingtone(); if (state != CallState.kRinging && state != CallState.kFledgling) { Logs().e('[VOIP] Call must be in \'ringing|fledgling\' state to reject!'); return; @@ -1133,7 +1134,7 @@ class CallSession { Future hangup([String? reason, bool suppressEvent = false]) async { // stop play ringtone - voip.delegate.stopRingtone(); + await voip.delegate.stopRingtone(); await terminate( CallParty.kLocal, reason ?? CallErrorCode.UserHangup, !suppressEvent); @@ -1171,7 +1172,7 @@ class CallSession { ringingTimer = null; try { - voip.delegate.stopRingtone(); + await voip.delegate.stopRingtone(); } catch (e) { // maybe rigntone never started (group calls) or has been stopped already Logs().d('stopping ringtone failed ', e); @@ -1195,10 +1196,10 @@ class CallSession { await cleanUp(); if (shouldEmit) { onCallHangup.add(this); - voip.delegate.handleCallEnded(this); + await voip.delegate.handleCallEnded(this); fireCallEvent(CallEvent.kHangup); if ((party == CallParty.kRemote && missedCall)) { - voip.delegate.handleMissedCall(this); + await voip.delegate.handleMissedCall(this); } } } @@ -1388,9 +1389,9 @@ class CallSession { localUserMediaStream!.isVideoMuted()) || remoteOnHold; - _setTracksEnabled(localUserMediaStream?.stream!.getAudioTracks() ?? [], + _setTracksEnabled(localUserMediaStream?.stream?.getAudioTracks() ?? [], !micShouldBeMuted); - _setTracksEnabled(localUserMediaStream?.stream!.getVideoTracks() ?? [], + _setTracksEnabled(localUserMediaStream?.stream?.getVideoTracks() ?? [], !vidShouldBeMuted); await sendSDPStreamMetadataChanged( @@ -1406,10 +1407,12 @@ class CallSession { SDPStreamMetadata _getLocalSDPStreamMetadata() { final sdpStreamMetadatas = {}; for (final wpstream in getLocalStreams) { - sdpStreamMetadatas[wpstream.stream!.id] = SDPStreamPurpose( - purpose: wpstream.purpose, - audio_muted: wpstream.audioMuted, - video_muted: wpstream.videoMuted); + if (wpstream.stream != null) { + sdpStreamMetadatas[wpstream.stream!.id] = SDPStreamPurpose( + purpose: wpstream.purpose, + audio_muted: wpstream.audioMuted, + video_muted: wpstream.videoMuted); + } } final metadata = SDPStreamMetadata(sdpStreamMetadatas); Logs().v('Got local SDPStreamMetadata ${metadata.toJson().toString()}'); @@ -1468,18 +1471,18 @@ class CallSession { 'sdpSemantics': 'unified-plan' }; final pc = await voip.delegate.createPeerConnection(configuration); - pc.onTrack = (RTCTrackEvent event) { + pc.onTrack = (RTCTrackEvent event) async { if (event.streams.isNotEmpty) { final stream = event.streams[0]; - _addRemoteStream(stream); - stream.getTracks().forEach((track) { - track.onEnded = () { + await _addRemoteStream(stream); + for (final track in stream.getTracks()) { + track.onEnded = () async { if (stream.getTracks().isEmpty) { Logs().d('[VOIP] detected a empty stream, removing it'); - _removeStream(stream); + await _removeStream(stream); } }; - }); + } } }; return pc; diff --git a/lib/src/voip/group_call.dart b/lib/src/voip/group_call.dart index 34fdb7e0..a263208d 100644 --- a/lib/src/voip/group_call.dart +++ b/lib/src/voip/group_call.dart @@ -381,7 +381,7 @@ class GroupCall { localUserMediaStream = newStream; await localUserMediaStream!.initialize(); - addUserMediaStream(newStream); + await addUserMediaStream(newStream); setState(GroupCallState.LocalCallFeedInitialized); @@ -430,7 +430,7 @@ class GroupCall { _callSubscription = voip.onIncomingCall.stream.listen(onIncomingCall); for (final call in calls) { - onIncomingCall(call); + await onIncomingCall(call); } // Set up participants for the members currently in the room. @@ -438,26 +438,26 @@ class GroupCall { final memberStateEvents = await getAllMemberStateEvents(); - memberStateEvents.forEach((stateEvent) { - onMemberStateChanged(stateEvent); - }); + for (final memberState in memberStateEvents) { + await onMemberStateChanged(memberState); + } onActiveSpeakerLoop(); voip.currentGroupCID = groupCallId; - voip.delegate.handleNewGroupCall(this); + await voip.delegate.handleNewGroupCall(this); } Future dispose() async { if (localUserMediaStream != null) { - removeUserMediaStream(localUserMediaStream!); + await removeUserMediaStream(localUserMediaStream!); localUserMediaStream = null; } if (localScreenshareStream != null) { await stopMediaStream(localScreenshareStream!.stream); - removeScreenshareStream(localScreenshareStream!); + await removeScreenshareStream(localScreenshareStream!); localScreenshareStream = null; localDesktopCapturerSourceId = null; } @@ -467,9 +467,10 @@ class GroupCall { await removeMemberStateEvent(); final callsCopy = calls.toList(); - callsCopy.forEach((element) { - removeCall(element, CallErrorCode.UserHangup); - }); + + for (final call in callsCopy) { + await removeCall(call, CallErrorCode.UserHangup); + } activeSpeaker = null; activeSpeakerLoopTimeout?.cancel(); @@ -480,7 +481,7 @@ class GroupCall { await dispose(); setState(GroupCallState.LocalCallFeedUninitialized); voip.currentGroupCID = null; - voip.delegate.handleGroupCallEnded(this); + await voip.delegate.handleGroupCallEnded(this); final justLeftGroupCall = voip.groupCalls.tryGet(room.id); // terminate group call if empty if (justLeftGroupCall != null && @@ -510,7 +511,7 @@ class GroupCall { }); Logs().d('[VOIP] Group call $groupCallId was killed'); } - voip.delegate.handleGroupCallEnded(this); + await voip.delegate.handleGroupCallEnded(this); setState(GroupCallState.Ended); } @@ -540,9 +541,9 @@ class GroupCall { setTracksEnabled(localUserMediaStream!.stream!.getAudioTracks(), !muted); } - calls.forEach((call) { - call.setMicrophoneMuted(muted); - }); + for (final call in calls) { + await call.setMicrophoneMuted(muted); + } onGroupCallEvent.add(GroupCallEvent.LocalMuteStateChanged); return true; @@ -558,9 +559,9 @@ class GroupCall { setTracksEnabled(localUserMediaStream!.stream!.getVideoTracks(), !muted); } - calls.forEach((call) { - call.setLocalVideoMuted(muted); - }); + for (final call in calls) { + await call.setLocalVideoMuted(muted); + } onGroupCallEvent.add(GroupCallEvent.LocalMuteStateChanged); return true; @@ -580,13 +581,13 @@ class GroupCall { try { Logs().v('Asking for screensharing permissions...'); final stream = await _getDisplayMedia(); - stream.getTracks().forEach((track) { - track.onEnded = () { - // screen sharing should only have 1 video track anyway, so this only - // fires once - setScreensharingEnabled(false, ''); + for (final track in stream.getTracks()) { + // screen sharing should only have 1 video track anyway, so this only + // fires once + track.onEnded = () async { + await setScreensharingEnabled(false, ''); }; - }); + } Logs().v( 'Screensharing permissions granted. Setting screensharing enabled on all calls'); localDesktopCapturerSourceId = desktopCapturerSourceId; @@ -607,12 +608,11 @@ class GroupCall { await localScreenshareStream!.initialize(); onGroupCallEvent.add(GroupCallEvent.LocalScreenshareStateChanged); - - calls.forEach((call) async { + for (final call in calls) { await call.addLocalStream( await localScreenshareStream!.stream!.clone(), localScreenshareStream!.purpose); - }); + } await sendMemberStateEvent(); @@ -625,11 +625,12 @@ class GroupCall { return false; } } else { - calls.forEach((call) { - call.removeLocalStream(call.localScreenSharingStream!); - }); + for (final call in calls) { + await call.removeLocalStream(call.localScreenSharingStream!); + } + await stopMediaStream(localScreenshareStream?.stream); - removeScreenshareStream(localScreenshareStream!); + await removeScreenshareStream(localScreenshareStream!); localScreenshareStream = null; localDesktopCapturerSourceId = null; await sendMemberStateEvent(); @@ -642,7 +643,7 @@ class GroupCall { return localScreenshareStream != null; } - void onIncomingCall(CallSession newCall) { + Future onIncomingCall(CallSession newCall) async { // The incoming calls may be for another room, which we will ignore. if (newCall.room.id != room.id) { return; @@ -656,7 +657,7 @@ class GroupCall { if (newCall.groupCallId == null || newCall.groupCallId != groupCallId) { Logs().v( 'Incoming call with groupCallId ${newCall.groupCallId} ignored because it doesn\'t match the current group call'); - newCall.reject(); + await newCall.reject(); return; } @@ -671,12 +672,12 @@ class GroupCall { // Check if the user calling has an existing call and use this call instead. if (existingCall != null) { - replaceCall(existingCall, newCall); + await replaceCall(existingCall, newCall); } else { - addCall(newCall); + await addCall(newCall); } - newCall.answerWithStreams(getLocalStreams()); + await newCall.answerWithStreams(getLocalStreams()); } Future sendMemberStateEvent() async { @@ -782,7 +783,7 @@ class GroupCall { room.id, EventTypes.GroupCallMemberPrefix, localUserId, content); } - void onMemberStateChanged(MatrixEvent event) async { + Future onMemberStateChanged(MatrixEvent event) async { // The member events may be received for another room, which we will ignore. if (event.roomId != room.id) { return; @@ -892,7 +893,7 @@ class GroupCall { await newCall.placeCallWithStreams( getLocalStreams(), requestScreenshareFeed); - addCall(newCall); + await addCall(newCall); } Future getDeviceForMember(String userId) async { @@ -929,13 +930,14 @@ class GroupCall { return null; } - void addCall(CallSession call) { + Future addCall(CallSession call) async { calls.add(call); - initCall(call); + await initCall(call); onGroupCallEvent.add(GroupCallEvent.CallsChanged); } - void replaceCall(CallSession existingCall, CallSession replacementCall) { + Future replaceCall( + CallSession existingCall, CallSession replacementCall) async { final existingCallIndex = calls.indexWhere((element) => element == existingCall); @@ -946,15 +948,15 @@ class GroupCall { calls.removeAt(existingCallIndex); calls.add(replacementCall); - disposeCall(existingCall, CallErrorCode.Replaced); - initCall(replacementCall); + await disposeCall(existingCall, CallErrorCode.Replaced); + await initCall(replacementCall); onGroupCallEvent.add(GroupCallEvent.CallsChanged); } /// Removes a peer call from group calls. - void removeCall(CallSession call, String hangupReason) { - disposeCall(call, hangupReason); + Future removeCall(CallSession call, String hangupReason) async { + await disposeCall(call, hangupReason); calls.removeWhere((element) => call.callId == element.callId); @@ -962,7 +964,7 @@ class GroupCall { } /// init a peer call from group calls. - void initCall(CallSession call) { + Future initCall(CallSession call) async { final opponentMemberId = call.opponentDeviceId; if (opponentMemberId == null) { @@ -972,17 +974,17 @@ class GroupCall { call.onCallStateChanged.stream .listen(((event) => onCallStateChanged(call, event))); - call.onCallReplaced.stream.listen((CallSession newCall) { - replaceCall(call, newCall); + call.onCallReplaced.stream.listen((CallSession newCall) async { + await replaceCall(call, newCall); }); - call.onCallStreamsChanged.stream.listen((call) { - call.tryRemoveStopedStreams(); - onStreamsChanged(call); + call.onCallStreamsChanged.stream.listen((call) async { + await call.tryRemoveStopedStreams(); + await onStreamsChanged(call); }); - call.onCallHangup.stream.listen((event) { - onCallHangup(call); + call.onCallHangup.stream.listen((event) async { + await onCallHangup(call); }); call.onStreamAdd.stream.listen((stream) { @@ -998,7 +1000,7 @@ class GroupCall { }); } - void disposeCall(CallSession call, String hangupReason) { + Future disposeCall(CallSession call, String hangupReason) async { final opponentMemberId = call.opponentDeviceId; if (opponentMemberId == null) { @@ -1012,19 +1014,19 @@ class GroupCall { } if (call.state != CallState.kEnded) { - call.hangup(hangupReason, false); + await call.hangup(hangupReason, false); } final usermediaStream = getUserMediaStreamByUserId(opponentMemberId); if (usermediaStream != null) { - removeUserMediaStream(usermediaStream); + await removeUserMediaStream(usermediaStream); } final screenshareStream = getScreenshareStreamByUserId(opponentMemberId); if (screenshareStream != null) { - removeScreenshareStream(screenshareStream); + await removeScreenshareStream(screenshareStream); } } @@ -1032,7 +1034,7 @@ class GroupCall { return call.remoteUser?.id ?? call.invitee; } - void onStreamsChanged(CallSession call) { + Future onStreamsChanged(CallSession call) async { final opponentMemberId = getCallUserId(call); if (opponentMemberId == null) { @@ -1045,13 +1047,14 @@ class GroupCall { if (remoteStreamChanged) { if (currentUserMediaStream == null && remoteUsermediaStream != null) { - addUserMediaStream(remoteUsermediaStream); + await addUserMediaStream(remoteUsermediaStream); } else if (currentUserMediaStream != null && remoteUsermediaStream != null) { - replaceUserMediaStream(currentUserMediaStream, remoteUsermediaStream); + await replaceUserMediaStream( + currentUserMediaStream, remoteUsermediaStream); } else if (currentUserMediaStream != null && remoteUsermediaStream == null) { - removeUserMediaStream(currentUserMediaStream); + await removeUserMediaStream(currentUserMediaStream); } } @@ -1067,38 +1070,38 @@ class GroupCall { addScreenshareStream(remoteScreensharingStream); } else if (currentScreenshareStream != null && remoteScreensharingStream != null) { - replaceScreenshareStream( + await replaceScreenshareStream( currentScreenshareStream, remoteScreensharingStream); } else if (currentScreenshareStream != null && remoteScreensharingStream == null) { - removeScreenshareStream(currentScreenshareStream); + await removeScreenshareStream(currentScreenshareStream); } } onGroupCallFeedsChanged.add(this); } - void onCallStateChanged(CallSession call, CallState state) { + Future onCallStateChanged(CallSession call, CallState state) async { final audioMuted = localUserMediaStream?.isAudioMuted() ?? true; if (call.localUserMediaStream != null && call.isMicrophoneMuted != audioMuted) { - call.setMicrophoneMuted(audioMuted); + await call.setMicrophoneMuted(audioMuted); } final videoMuted = localUserMediaStream?.isVideoMuted() ?? true; if (call.localUserMediaStream != null && call.isLocalVideoMuted != videoMuted) { - call.setLocalVideoMuted(videoMuted); + await call.setLocalVideoMuted(videoMuted); } } - void onCallHangup(CallSession call) { + Future onCallHangup(CallSession call) async { if (call.hangupReason == CallErrorCode.Replaced) { return; } - onStreamsChanged(call); - removeCall(call, call.hangupReason!); + await onStreamsChanged(call); + await removeCall(call, call.hangupReason!); } WrappedMediaStream? getUserMediaStreamByUserId(String userId) { @@ -1109,7 +1112,7 @@ class GroupCall { return null; } - void addUserMediaStream(WrappedMediaStream stream) { + Future addUserMediaStream(WrappedMediaStream stream) async { userMediaStreams.add(stream); //callFeed.measureVolumeActivity(true); onStreamAdd.add(stream); @@ -1132,7 +1135,7 @@ class GroupCall { onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged); } - void removeUserMediaStream(WrappedMediaStream stream) { + Future removeUserMediaStream(WrappedMediaStream stream) async { final streamIndex = userMediaStreams.indexWhere((stream) => stream.userId == stream.userId); @@ -1145,8 +1148,8 @@ class GroupCall { onStreamRemoved.add(stream); if (stream.isLocal()) { - stream.disposeRenderer(); - stopMediaStream(stream.stream); + await stream.disposeRenderer(); + await stopMediaStream(stream.stream); } onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged); @@ -1240,7 +1243,7 @@ class GroupCall { onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged); } - void removeScreenshareStream(WrappedMediaStream stream) { + Future removeScreenshareStream(WrappedMediaStream stream) async { final streamIndex = screenshareStreams .indexWhere((stream) => stream.userId == stream.userId); @@ -1254,8 +1257,8 @@ class GroupCall { onStreamRemoved.add(stream); if (stream.isLocal()) { - stream.disposeRenderer(); - stopMediaStream(stream.stream); + await stream.disposeRenderer(); + await stopMediaStream(stream.stream); } onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged); diff --git a/lib/src/voip/utils.dart b/lib/src/voip/utils.dart index 2c4278ee..9179399e 100644 --- a/lib/src/voip/utils.dart +++ b/lib/src/voip/utils.dart @@ -1,13 +1,24 @@ import 'dart:async'; +import 'package:matrix/matrix.dart'; import 'package:random_string/random_string.dart'; import 'package:webrtc_interface/webrtc_interface.dart'; Future stopMediaStream(MediaStream? stream) async { - stream?.getTracks().forEach((element) async { - await element.stop(); - }); - await stream?.dispose(); + if (stream != null) { + for (final track in stream.getTracks()) { + try { + await track.stop(); + } catch (e) { + Logs().e('[VOIP] stopping track ${track.id} failed', e); + } + } + try { + await stream.dispose(); + } catch (e) { + Logs().e('[VOIP] disposing stream ${stream.id} failed', e); + } + } } void setTracksEnabled(List tracks, bool enabled) { diff --git a/lib/src/voip/voip.dart b/lib/src/voip/voip.dart index 1a719cb9..e584a781 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -14,13 +14,13 @@ abstract class WebRTCDelegate { Map configuration, [Map constraints = const {}]); VideoRenderer createRenderer(); - void playRingtone(); - void stopRingtone(); - void handleNewCall(CallSession session); - void handleCallEnded(CallSession session); - void handleMissedCall(CallSession session); - void handleNewGroupCall(GroupCall groupCall); - void handleGroupCallEnded(GroupCall groupCall); + Future playRingtone(); + Future stopRingtone(); + Future handleNewCall(CallSession session); + Future handleCallEnded(CallSession session); + Future handleMissedCall(CallSession session); + Future handleNewGroupCall(GroupCall groupCall); + Future handleGroupCallEnded(GroupCall groupCall); bool get isWeb; /// This should be set to false if any calls in the client are in kConnected @@ -71,13 +71,13 @@ class VoIP { .listen((event) => _handleEvent(event, onAssertedIdentityReceived)); client.onRoomState.stream.listen( - (event) { + (event) async { if ([ EventTypes.GroupCallPrefix, EventTypes.GroupCallMemberPrefix, ].contains(event.type)) { Logs().v('[VOIP] onRoomState: type ${event.toJson()}.'); - onRoomStateChanged(event); + await onRoomStateChanged(event); } }, ); @@ -237,7 +237,7 @@ class VoIP { Logs().v( '[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.'); await newCall.reject(reason: CallErrorCode.UserBusy, shouldEmit: false); - delegate.handleMissedCall(newCall); + await delegate.handleMissedCall(newCall); return; } @@ -254,7 +254,7 @@ class VoIP { /// Autoplay on firefox still needs interaction, without which all notifications /// could be blocked. if (confId == null) { - delegate.playRingtone(); + await delegate.playRingtone(); } await newCall.initWithInvite( @@ -264,7 +264,7 @@ class VoIP { // Popup CallingPage for incoming call. if (confId == null && !newCall.callHasEnded) { - delegate.handleNewCall(newCall); + await delegate.handleNewCall(newCall); } onIncomingCall.add(newCall); @@ -281,7 +281,7 @@ class VoIP { if (senderId == client.userID) { // Ignore messages to yourself. if (!call.answeredByUs) { - delegate.stopRingtone(); + await delegate.stopRingtone(); } if (call.state == CallState.kRinging) { call.onAnsweredElsewhere(); @@ -333,7 +333,7 @@ class VoIP { Future onCallHangup(String roomId, String _ /*senderId unused*/, Map content) async { // stop play ringtone, if this is an incoming call - delegate.stopRingtone(); + await delegate.stopRingtone(); Logs().v('[VOIP] onCallHangup => ${content.toString()}'); final String callId = content['call_id']; final call = calls[callId]; @@ -676,14 +676,16 @@ class VoIP { Future createGroupCallForRoom(Room room) async { final events = await client.getRoomState(room.id); events.sort((a, b) => a.originServerTs.compareTo(b.originServerTs)); - events.forEach((element) async { - if (element.type == EventTypes.GroupCallPrefix) { - if (element.content['m.terminated'] != null) { + + for (final event in events) { + if (event.type == EventTypes.GroupCallPrefix) { + if (event.content['m.terminated'] != null) { return; } - await createGroupCallFromRoomStateEvent(element); + await createGroupCallFromRoomStateEvent(event); } - }); + } + return; } @@ -733,12 +735,12 @@ class VoIP { onIncomingGroupCall.add(groupCall); if (emitHandleNewGroupCall) { - delegate.handleNewGroupCall(groupCall); + await delegate.handleNewGroupCall(groupCall); } return groupCall; } - void onRoomStateChanged(MatrixEvent event) { + Future onRoomStateChanged(MatrixEvent event) async { final eventType = event.type; final roomId = event.roomId; if (eventType == EventTypes.GroupCallPrefix) { @@ -746,11 +748,11 @@ class VoIP { final content = event.content; final currentGroupCall = groupCalls[groupCallId]; if (currentGroupCall == null && content['m.terminated'] == null) { - createGroupCallFromRoomStateEvent(event); + await createGroupCallFromRoomStateEvent(event); } else if (currentGroupCall != null && currentGroupCall.groupCallId == groupCallId) { if (content['m.terminated'] != null) { - currentGroupCall.terminate(emitStateEvent: false); + await currentGroupCall.terminate(emitStateEvent: false); } else if (content['m.type'] != currentGroupCall.type) { // TODO: Handle the callType changing when the room state changes Logs().w( @@ -767,7 +769,7 @@ class VoIP { if (groupCall == null) { return; } - groupCall.onMemberStateChanged(event); + await groupCall.onMemberStateChanged(event); } } diff --git a/lib/src/voip/voip_room_extension.dart b/lib/src/voip/voip_room_extension.dart index f52137e0..0a1b1532 100644 --- a/lib/src/voip/voip_room_extension.dart +++ b/lib/src/voip/voip_room_extension.dart @@ -87,37 +87,38 @@ extension GroupCallUtils on Room { final copyGroupCallIds = states.tryGetMap(EventTypes.GroupCallPrefix); if (copyGroupCallIds == null) return; - copyGroupCallIds.forEach( - (groupCallId, groupCallEvent) async { - if (groupCallEvent.content.tryGet('m.intent') == 'm.room') return; - if (!groupCallEvent.content.containsKey('m.terminated')) { - Logs().i('found non terminated group call with id $groupCallId'); - // call is not empty but check for stale participants (gone offline) - // with expire_ts - bool callExpired = true; // assume call is expired - final callMemberEvents = - states.tryGetMap(EventTypes.GroupCallMemberPrefix); - if (callMemberEvents != null) { - for (var i = 0; i < callMemberEvents.length; i++) { - final groupCallMemberEventMap = - callMemberEvents.entries.toList()[i]; + for (final groupCall in copyGroupCallIds.entries) { + final groupCallId = groupCall.key; + final groupCallEvent = groupCall.value; - final groupCallMemberEvent = groupCallMemberEventMap.value; - callExpired = - callMemberStateIsExpired(groupCallMemberEvent, groupCallId); - // no need to iterate further even if one participant says call isn't expired - if (!callExpired) break; - } - } + if (groupCallEvent.content.tryGet('m.intent') == 'm.room') return; + if (!groupCallEvent.content.containsKey('m.terminated')) { + Logs().i('found non terminated group call with id $groupCallId'); + // call is not empty but check for stale participants (gone offline) + // with expire_ts + bool callExpired = true; // assume call is expired + final callMemberEvents = + states.tryGetMap(EventTypes.GroupCallMemberPrefix); + if (callMemberEvents != null) { + for (var i = 0; i < callMemberEvents.length; i++) { + final groupCallMemberEventMap = + callMemberEvents.entries.toList()[i]; - if (callExpired) { - Logs().i( - 'Group call with only expired timestamps detected, terminating'); - await sendGroupCallTerminateEvent(groupCallId); + final groupCallMemberEvent = groupCallMemberEventMap.value; + callExpired = + callMemberStateIsExpired(groupCallMemberEvent, groupCallId); + // no need to iterate further even if one participant says call isn't expired + if (!callExpired) break; } } - }, - ); + + if (callExpired) { + Logs().i( + 'Group call with only expired timestamps detected, terminating'); + await sendGroupCallTerminateEvent(groupCallId); + } + } + } } /// returns the event_id if successful From 9cb99377fbd571d14f2afe7700a7f96ee0597944 Mon Sep 17 00:00:00 2001 From: td Date: Fri, 24 Feb 2023 22:31:10 +0530 Subject: [PATCH 2/2] fix: make group call stuff async, let clients await what they need --- lib/src/voip/utils.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/voip/utils.dart b/lib/src/voip/utils.dart index 9179399e..8e35e3af 100644 --- a/lib/src/voip/utils.dart +++ b/lib/src/voip/utils.dart @@ -1,9 +1,10 @@ import 'dart:async'; -import 'package:matrix/matrix.dart'; import 'package:random_string/random_string.dart'; import 'package:webrtc_interface/webrtc_interface.dart'; +import 'package:matrix/matrix.dart'; + Future stopMediaStream(MediaStream? stream) async { if (stream != null) { for (final track in stream.getTracks()) {