fix: make group call stuff async, let clients await what they need

This commit is contained in:
td 2023-02-24 22:17:27 +05:30
parent c5aa899038
commit 2d0fd9c393
No known key found for this signature in database
GPG Key ID: F6D9E9BF14C7D103
5 changed files with 176 additions and 156 deletions

View File

@ -500,10 +500,10 @@ class CallSession {
}); });
} }
void answerWithStreams(List<WrappedMediaStream> callFeeds) { Future<void> answerWithStreams(List<WrappedMediaStream> callFeeds) async {
if (inviteOrAnswerSent) return; if (inviteOrAnswerSent) return;
Logs().d('nswering call $callId'); Logs().d('nswering call $callId');
gotCallFeedsForAnswer(callFeeds); await gotCallFeedsForAnswer(callFeeds);
} }
void replacedBy(CallSession newCall) { void replacedBy(CallSession newCall) {
@ -768,13 +768,14 @@ class CallSession {
if (stream == null) { if (stream == null) {
return false; return false;
} }
stream.getTracks().forEach((track) { for (final track in stream.getTracks()) {
// screen sharing should only have 1 video track anyway, so this only // screen sharing should only have 1 video track anyway, so this only
// fires once // fires once
track.onEnded = () { track.onEnded = () async {
setScreensharingEnabled(false); await setScreensharingEnabled(false);
}; };
}); }
await addLocalStream(stream, SDPStreamMetadataPurpose.Screenshare); await addLocalStream(stream, SDPStreamMetadataPurpose.Screenshare);
return true; return true;
} catch (err) { } catch (err) {
@ -1068,7 +1069,7 @@ class CallSession {
return; return;
} }
// stop play ringtone // stop play ringtone
voip.delegate.stopRingtone(); await voip.delegate.stopRingtone();
if (direction == CallDirection.kIncoming) { if (direction == CallDirection.kIncoming) {
setCallState(CallState.kCreateAnswer); setCallState(CallState.kCreateAnswer);
@ -1118,7 +1119,7 @@ class CallSession {
/// ///
Future<void> reject({String? reason, bool shouldEmit = true}) async { Future<void> reject({String? reason, bool shouldEmit = true}) async {
// stop play ringtone // stop play ringtone
voip.delegate.stopRingtone(); await voip.delegate.stopRingtone();
if (state != CallState.kRinging && state != CallState.kFledgling) { if (state != CallState.kRinging && state != CallState.kFledgling) {
Logs().e('[VOIP] Call must be in \'ringing|fledgling\' state to reject!'); Logs().e('[VOIP] Call must be in \'ringing|fledgling\' state to reject!');
return; return;
@ -1133,7 +1134,7 @@ class CallSession {
Future<void> hangup([String? reason, bool suppressEvent = false]) async { Future<void> hangup([String? reason, bool suppressEvent = false]) async {
// stop play ringtone // stop play ringtone
voip.delegate.stopRingtone(); await voip.delegate.stopRingtone();
await terminate( await terminate(
CallParty.kLocal, reason ?? CallErrorCode.UserHangup, !suppressEvent); CallParty.kLocal, reason ?? CallErrorCode.UserHangup, !suppressEvent);
@ -1171,7 +1172,7 @@ class CallSession {
ringingTimer = null; ringingTimer = null;
try { try {
voip.delegate.stopRingtone(); await voip.delegate.stopRingtone();
} catch (e) { } catch (e) {
// maybe rigntone never started (group calls) or has been stopped already // maybe rigntone never started (group calls) or has been stopped already
Logs().d('stopping ringtone failed ', e); Logs().d('stopping ringtone failed ', e);
@ -1195,10 +1196,10 @@ class CallSession {
await cleanUp(); await cleanUp();
if (shouldEmit) { if (shouldEmit) {
onCallHangup.add(this); onCallHangup.add(this);
voip.delegate.handleCallEnded(this); await voip.delegate.handleCallEnded(this);
fireCallEvent(CallEvent.kHangup); fireCallEvent(CallEvent.kHangup);
if ((party == CallParty.kRemote && missedCall)) { if ((party == CallParty.kRemote && missedCall)) {
voip.delegate.handleMissedCall(this); await voip.delegate.handleMissedCall(this);
} }
} }
} }
@ -1388,9 +1389,9 @@ class CallSession {
localUserMediaStream!.isVideoMuted()) || localUserMediaStream!.isVideoMuted()) ||
remoteOnHold; remoteOnHold;
_setTracksEnabled(localUserMediaStream?.stream!.getAudioTracks() ?? [], _setTracksEnabled(localUserMediaStream?.stream?.getAudioTracks() ?? [],
!micShouldBeMuted); !micShouldBeMuted);
_setTracksEnabled(localUserMediaStream?.stream!.getVideoTracks() ?? [], _setTracksEnabled(localUserMediaStream?.stream?.getVideoTracks() ?? [],
!vidShouldBeMuted); !vidShouldBeMuted);
await sendSDPStreamMetadataChanged( await sendSDPStreamMetadataChanged(
@ -1406,11 +1407,13 @@ class CallSession {
SDPStreamMetadata _getLocalSDPStreamMetadata() { SDPStreamMetadata _getLocalSDPStreamMetadata() {
final sdpStreamMetadatas = <String, SDPStreamPurpose>{}; final sdpStreamMetadatas = <String, SDPStreamPurpose>{};
for (final wpstream in getLocalStreams) { for (final wpstream in getLocalStreams) {
if (wpstream.stream != null) {
sdpStreamMetadatas[wpstream.stream!.id] = SDPStreamPurpose( sdpStreamMetadatas[wpstream.stream!.id] = SDPStreamPurpose(
purpose: wpstream.purpose, purpose: wpstream.purpose,
audio_muted: wpstream.audioMuted, audio_muted: wpstream.audioMuted,
video_muted: wpstream.videoMuted); video_muted: wpstream.videoMuted);
} }
}
final metadata = SDPStreamMetadata(sdpStreamMetadatas); final metadata = SDPStreamMetadata(sdpStreamMetadatas);
Logs().v('Got local SDPStreamMetadata ${metadata.toJson().toString()}'); Logs().v('Got local SDPStreamMetadata ${metadata.toJson().toString()}');
return metadata; return metadata;
@ -1468,18 +1471,18 @@ class CallSession {
'sdpSemantics': 'unified-plan' 'sdpSemantics': 'unified-plan'
}; };
final pc = await voip.delegate.createPeerConnection(configuration); final pc = await voip.delegate.createPeerConnection(configuration);
pc.onTrack = (RTCTrackEvent event) { pc.onTrack = (RTCTrackEvent event) async {
if (event.streams.isNotEmpty) { if (event.streams.isNotEmpty) {
final stream = event.streams[0]; final stream = event.streams[0];
_addRemoteStream(stream); await _addRemoteStream(stream);
stream.getTracks().forEach((track) { for (final track in stream.getTracks()) {
track.onEnded = () { track.onEnded = () async {
if (stream.getTracks().isEmpty) { if (stream.getTracks().isEmpty) {
Logs().d('[VOIP] detected a empty stream, removing it'); Logs().d('[VOIP] detected a empty stream, removing it');
_removeStream(stream); await _removeStream(stream);
} }
}; };
}); }
} }
}; };
return pc; return pc;

View File

@ -381,7 +381,7 @@ class GroupCall {
localUserMediaStream = newStream; localUserMediaStream = newStream;
await localUserMediaStream!.initialize(); await localUserMediaStream!.initialize();
addUserMediaStream(newStream); await addUserMediaStream(newStream);
setState(GroupCallState.LocalCallFeedInitialized); setState(GroupCallState.LocalCallFeedInitialized);
@ -430,7 +430,7 @@ class GroupCall {
_callSubscription = voip.onIncomingCall.stream.listen(onIncomingCall); _callSubscription = voip.onIncomingCall.stream.listen(onIncomingCall);
for (final call in calls) { for (final call in calls) {
onIncomingCall(call); await onIncomingCall(call);
} }
// Set up participants for the members currently in the room. // Set up participants for the members currently in the room.
@ -438,26 +438,26 @@ class GroupCall {
final memberStateEvents = await getAllMemberStateEvents(); final memberStateEvents = await getAllMemberStateEvents();
memberStateEvents.forEach((stateEvent) { for (final memberState in memberStateEvents) {
onMemberStateChanged(stateEvent); await onMemberStateChanged(memberState);
}); }
onActiveSpeakerLoop(); onActiveSpeakerLoop();
voip.currentGroupCID = groupCallId; voip.currentGroupCID = groupCallId;
voip.delegate.handleNewGroupCall(this); await voip.delegate.handleNewGroupCall(this);
} }
Future<void> dispose() async { Future<void> dispose() async {
if (localUserMediaStream != null) { if (localUserMediaStream != null) {
removeUserMediaStream(localUserMediaStream!); await removeUserMediaStream(localUserMediaStream!);
localUserMediaStream = null; localUserMediaStream = null;
} }
if (localScreenshareStream != null) { if (localScreenshareStream != null) {
await stopMediaStream(localScreenshareStream!.stream); await stopMediaStream(localScreenshareStream!.stream);
removeScreenshareStream(localScreenshareStream!); await removeScreenshareStream(localScreenshareStream!);
localScreenshareStream = null; localScreenshareStream = null;
localDesktopCapturerSourceId = null; localDesktopCapturerSourceId = null;
} }
@ -467,9 +467,10 @@ class GroupCall {
await removeMemberStateEvent(); await removeMemberStateEvent();
final callsCopy = calls.toList(); final callsCopy = calls.toList();
callsCopy.forEach((element) {
removeCall(element, CallErrorCode.UserHangup); for (final call in callsCopy) {
}); await removeCall(call, CallErrorCode.UserHangup);
}
activeSpeaker = null; activeSpeaker = null;
activeSpeakerLoopTimeout?.cancel(); activeSpeakerLoopTimeout?.cancel();
@ -480,7 +481,7 @@ class GroupCall {
await dispose(); await dispose();
setState(GroupCallState.LocalCallFeedUninitialized); setState(GroupCallState.LocalCallFeedUninitialized);
voip.currentGroupCID = null; voip.currentGroupCID = null;
voip.delegate.handleGroupCallEnded(this); await voip.delegate.handleGroupCallEnded(this);
final justLeftGroupCall = voip.groupCalls.tryGet<GroupCall>(room.id); final justLeftGroupCall = voip.groupCalls.tryGet<GroupCall>(room.id);
// terminate group call if empty // terminate group call if empty
if (justLeftGroupCall != null && if (justLeftGroupCall != null &&
@ -510,7 +511,7 @@ class GroupCall {
}); });
Logs().d('[VOIP] Group call $groupCallId was killed'); Logs().d('[VOIP] Group call $groupCallId was killed');
} }
voip.delegate.handleGroupCallEnded(this); await voip.delegate.handleGroupCallEnded(this);
setState(GroupCallState.Ended); setState(GroupCallState.Ended);
} }
@ -540,9 +541,9 @@ class GroupCall {
setTracksEnabled(localUserMediaStream!.stream!.getAudioTracks(), !muted); setTracksEnabled(localUserMediaStream!.stream!.getAudioTracks(), !muted);
} }
calls.forEach((call) { for (final call in calls) {
call.setMicrophoneMuted(muted); await call.setMicrophoneMuted(muted);
}); }
onGroupCallEvent.add(GroupCallEvent.LocalMuteStateChanged); onGroupCallEvent.add(GroupCallEvent.LocalMuteStateChanged);
return true; return true;
@ -558,9 +559,9 @@ class GroupCall {
setTracksEnabled(localUserMediaStream!.stream!.getVideoTracks(), !muted); setTracksEnabled(localUserMediaStream!.stream!.getVideoTracks(), !muted);
} }
calls.forEach((call) { for (final call in calls) {
call.setLocalVideoMuted(muted); await call.setLocalVideoMuted(muted);
}); }
onGroupCallEvent.add(GroupCallEvent.LocalMuteStateChanged); onGroupCallEvent.add(GroupCallEvent.LocalMuteStateChanged);
return true; return true;
@ -580,13 +581,13 @@ class GroupCall {
try { try {
Logs().v('Asking for screensharing permissions...'); Logs().v('Asking for screensharing permissions...');
final stream = await _getDisplayMedia(); final stream = await _getDisplayMedia();
stream.getTracks().forEach((track) { for (final track in stream.getTracks()) {
track.onEnded = () {
// screen sharing should only have 1 video track anyway, so this only // screen sharing should only have 1 video track anyway, so this only
// fires once // fires once
setScreensharingEnabled(false, ''); track.onEnded = () async {
await setScreensharingEnabled(false, '');
}; };
}); }
Logs().v( Logs().v(
'Screensharing permissions granted. Setting screensharing enabled on all calls'); 'Screensharing permissions granted. Setting screensharing enabled on all calls');
localDesktopCapturerSourceId = desktopCapturerSourceId; localDesktopCapturerSourceId = desktopCapturerSourceId;
@ -607,12 +608,11 @@ class GroupCall {
await localScreenshareStream!.initialize(); await localScreenshareStream!.initialize();
onGroupCallEvent.add(GroupCallEvent.LocalScreenshareStateChanged); onGroupCallEvent.add(GroupCallEvent.LocalScreenshareStateChanged);
for (final call in calls) {
calls.forEach((call) async {
await call.addLocalStream( await call.addLocalStream(
await localScreenshareStream!.stream!.clone(), await localScreenshareStream!.stream!.clone(),
localScreenshareStream!.purpose); localScreenshareStream!.purpose);
}); }
await sendMemberStateEvent(); await sendMemberStateEvent();
@ -625,11 +625,12 @@ class GroupCall {
return false; return false;
} }
} else { } else {
calls.forEach((call) { for (final call in calls) {
call.removeLocalStream(call.localScreenSharingStream!); await call.removeLocalStream(call.localScreenSharingStream!);
}); }
await stopMediaStream(localScreenshareStream?.stream); await stopMediaStream(localScreenshareStream?.stream);
removeScreenshareStream(localScreenshareStream!); await removeScreenshareStream(localScreenshareStream!);
localScreenshareStream = null; localScreenshareStream = null;
localDesktopCapturerSourceId = null; localDesktopCapturerSourceId = null;
await sendMemberStateEvent(); await sendMemberStateEvent();
@ -642,7 +643,7 @@ class GroupCall {
return localScreenshareStream != null; return localScreenshareStream != null;
} }
void onIncomingCall(CallSession newCall) { Future<void> onIncomingCall(CallSession newCall) async {
// The incoming calls may be for another room, which we will ignore. // The incoming calls may be for another room, which we will ignore.
if (newCall.room.id != room.id) { if (newCall.room.id != room.id) {
return; return;
@ -656,7 +657,7 @@ class GroupCall {
if (newCall.groupCallId == null || newCall.groupCallId != groupCallId) { if (newCall.groupCallId == null || newCall.groupCallId != groupCallId) {
Logs().v( Logs().v(
'Incoming call with groupCallId ${newCall.groupCallId} ignored because it doesn\'t match the current group call'); 'Incoming call with groupCallId ${newCall.groupCallId} ignored because it doesn\'t match the current group call');
newCall.reject(); await newCall.reject();
return; return;
} }
@ -671,12 +672,12 @@ class GroupCall {
// Check if the user calling has an existing call and use this call instead. // Check if the user calling has an existing call and use this call instead.
if (existingCall != null) { if (existingCall != null) {
replaceCall(existingCall, newCall); await replaceCall(existingCall, newCall);
} else { } else {
addCall(newCall); await addCall(newCall);
} }
newCall.answerWithStreams(getLocalStreams()); await newCall.answerWithStreams(getLocalStreams());
} }
Future<void> sendMemberStateEvent() async { Future<void> sendMemberStateEvent() async {
@ -782,7 +783,7 @@ class GroupCall {
room.id, EventTypes.GroupCallMemberPrefix, localUserId, content); room.id, EventTypes.GroupCallMemberPrefix, localUserId, content);
} }
void onMemberStateChanged(MatrixEvent event) async { Future<void> onMemberStateChanged(MatrixEvent event) async {
// The member events may be received for another room, which we will ignore. // The member events may be received for another room, which we will ignore.
if (event.roomId != room.id) { if (event.roomId != room.id) {
return; return;
@ -892,7 +893,7 @@ class GroupCall {
await newCall.placeCallWithStreams( await newCall.placeCallWithStreams(
getLocalStreams(), requestScreenshareFeed); getLocalStreams(), requestScreenshareFeed);
addCall(newCall); await addCall(newCall);
} }
Future<IGroupCallRoomMemberDevice?> getDeviceForMember(String userId) async { Future<IGroupCallRoomMemberDevice?> getDeviceForMember(String userId) async {
@ -929,13 +930,14 @@ class GroupCall {
return null; return null;
} }
void addCall(CallSession call) { Future<void> addCall(CallSession call) async {
calls.add(call); calls.add(call);
initCall(call); await initCall(call);
onGroupCallEvent.add(GroupCallEvent.CallsChanged); onGroupCallEvent.add(GroupCallEvent.CallsChanged);
} }
void replaceCall(CallSession existingCall, CallSession replacementCall) { Future<void> replaceCall(
CallSession existingCall, CallSession replacementCall) async {
final existingCallIndex = final existingCallIndex =
calls.indexWhere((element) => element == existingCall); calls.indexWhere((element) => element == existingCall);
@ -946,15 +948,15 @@ class GroupCall {
calls.removeAt(existingCallIndex); calls.removeAt(existingCallIndex);
calls.add(replacementCall); calls.add(replacementCall);
disposeCall(existingCall, CallErrorCode.Replaced); await disposeCall(existingCall, CallErrorCode.Replaced);
initCall(replacementCall); await initCall(replacementCall);
onGroupCallEvent.add(GroupCallEvent.CallsChanged); onGroupCallEvent.add(GroupCallEvent.CallsChanged);
} }
/// Removes a peer call from group calls. /// Removes a peer call from group calls.
void removeCall(CallSession call, String hangupReason) { Future<void> removeCall(CallSession call, String hangupReason) async {
disposeCall(call, hangupReason); await disposeCall(call, hangupReason);
calls.removeWhere((element) => call.callId == element.callId); calls.removeWhere((element) => call.callId == element.callId);
@ -962,7 +964,7 @@ class GroupCall {
} }
/// init a peer call from group calls. /// init a peer call from group calls.
void initCall(CallSession call) { Future<void> initCall(CallSession call) async {
final opponentMemberId = call.opponentDeviceId; final opponentMemberId = call.opponentDeviceId;
if (opponentMemberId == null) { if (opponentMemberId == null) {
@ -972,17 +974,17 @@ class GroupCall {
call.onCallStateChanged.stream call.onCallStateChanged.stream
.listen(((event) => onCallStateChanged(call, event))); .listen(((event) => onCallStateChanged(call, event)));
call.onCallReplaced.stream.listen((CallSession newCall) { call.onCallReplaced.stream.listen((CallSession newCall) async {
replaceCall(call, newCall); await replaceCall(call, newCall);
}); });
call.onCallStreamsChanged.stream.listen((call) { call.onCallStreamsChanged.stream.listen((call) async {
call.tryRemoveStopedStreams(); await call.tryRemoveStopedStreams();
onStreamsChanged(call); await onStreamsChanged(call);
}); });
call.onCallHangup.stream.listen((event) { call.onCallHangup.stream.listen((event) async {
onCallHangup(call); await onCallHangup(call);
}); });
call.onStreamAdd.stream.listen((stream) { call.onStreamAdd.stream.listen((stream) {
@ -998,7 +1000,7 @@ class GroupCall {
}); });
} }
void disposeCall(CallSession call, String hangupReason) { Future<void> disposeCall(CallSession call, String hangupReason) async {
final opponentMemberId = call.opponentDeviceId; final opponentMemberId = call.opponentDeviceId;
if (opponentMemberId == null) { if (opponentMemberId == null) {
@ -1012,19 +1014,19 @@ class GroupCall {
} }
if (call.state != CallState.kEnded) { if (call.state != CallState.kEnded) {
call.hangup(hangupReason, false); await call.hangup(hangupReason, false);
} }
final usermediaStream = getUserMediaStreamByUserId(opponentMemberId); final usermediaStream = getUserMediaStreamByUserId(opponentMemberId);
if (usermediaStream != null) { if (usermediaStream != null) {
removeUserMediaStream(usermediaStream); await removeUserMediaStream(usermediaStream);
} }
final screenshareStream = getScreenshareStreamByUserId(opponentMemberId); final screenshareStream = getScreenshareStreamByUserId(opponentMemberId);
if (screenshareStream != null) { if (screenshareStream != null) {
removeScreenshareStream(screenshareStream); await removeScreenshareStream(screenshareStream);
} }
} }
@ -1032,7 +1034,7 @@ class GroupCall {
return call.remoteUser?.id ?? call.invitee; return call.remoteUser?.id ?? call.invitee;
} }
void onStreamsChanged(CallSession call) { Future<void> onStreamsChanged(CallSession call) async {
final opponentMemberId = getCallUserId(call); final opponentMemberId = getCallUserId(call);
if (opponentMemberId == null) { if (opponentMemberId == null) {
@ -1045,13 +1047,14 @@ class GroupCall {
if (remoteStreamChanged) { if (remoteStreamChanged) {
if (currentUserMediaStream == null && remoteUsermediaStream != null) { if (currentUserMediaStream == null && remoteUsermediaStream != null) {
addUserMediaStream(remoteUsermediaStream); await addUserMediaStream(remoteUsermediaStream);
} else if (currentUserMediaStream != null && } else if (currentUserMediaStream != null &&
remoteUsermediaStream != null) { remoteUsermediaStream != null) {
replaceUserMediaStream(currentUserMediaStream, remoteUsermediaStream); await replaceUserMediaStream(
currentUserMediaStream, remoteUsermediaStream);
} else if (currentUserMediaStream != null && } else if (currentUserMediaStream != null &&
remoteUsermediaStream == null) { remoteUsermediaStream == null) {
removeUserMediaStream(currentUserMediaStream); await removeUserMediaStream(currentUserMediaStream);
} }
} }
@ -1067,38 +1070,38 @@ class GroupCall {
addScreenshareStream(remoteScreensharingStream); addScreenshareStream(remoteScreensharingStream);
} else if (currentScreenshareStream != null && } else if (currentScreenshareStream != null &&
remoteScreensharingStream != null) { remoteScreensharingStream != null) {
replaceScreenshareStream( await replaceScreenshareStream(
currentScreenshareStream, remoteScreensharingStream); currentScreenshareStream, remoteScreensharingStream);
} else if (currentScreenshareStream != null && } else if (currentScreenshareStream != null &&
remoteScreensharingStream == null) { remoteScreensharingStream == null) {
removeScreenshareStream(currentScreenshareStream); await removeScreenshareStream(currentScreenshareStream);
} }
} }
onGroupCallFeedsChanged.add(this); onGroupCallFeedsChanged.add(this);
} }
void onCallStateChanged(CallSession call, CallState state) { Future<void> onCallStateChanged(CallSession call, CallState state) async {
final audioMuted = localUserMediaStream?.isAudioMuted() ?? true; final audioMuted = localUserMediaStream?.isAudioMuted() ?? true;
if (call.localUserMediaStream != null && if (call.localUserMediaStream != null &&
call.isMicrophoneMuted != audioMuted) { call.isMicrophoneMuted != audioMuted) {
call.setMicrophoneMuted(audioMuted); await call.setMicrophoneMuted(audioMuted);
} }
final videoMuted = localUserMediaStream?.isVideoMuted() ?? true; final videoMuted = localUserMediaStream?.isVideoMuted() ?? true;
if (call.localUserMediaStream != null && if (call.localUserMediaStream != null &&
call.isLocalVideoMuted != videoMuted) { call.isLocalVideoMuted != videoMuted) {
call.setLocalVideoMuted(videoMuted); await call.setLocalVideoMuted(videoMuted);
} }
} }
void onCallHangup(CallSession call) { Future<void> onCallHangup(CallSession call) async {
if (call.hangupReason == CallErrorCode.Replaced) { if (call.hangupReason == CallErrorCode.Replaced) {
return; return;
} }
onStreamsChanged(call); await onStreamsChanged(call);
removeCall(call, call.hangupReason!); await removeCall(call, call.hangupReason!);
} }
WrappedMediaStream? getUserMediaStreamByUserId(String userId) { WrappedMediaStream? getUserMediaStreamByUserId(String userId) {
@ -1109,7 +1112,7 @@ class GroupCall {
return null; return null;
} }
void addUserMediaStream(WrappedMediaStream stream) { Future<void> addUserMediaStream(WrappedMediaStream stream) async {
userMediaStreams.add(stream); userMediaStreams.add(stream);
//callFeed.measureVolumeActivity(true); //callFeed.measureVolumeActivity(true);
onStreamAdd.add(stream); onStreamAdd.add(stream);
@ -1132,7 +1135,7 @@ class GroupCall {
onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged); onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged);
} }
void removeUserMediaStream(WrappedMediaStream stream) { Future<void> removeUserMediaStream(WrappedMediaStream stream) async {
final streamIndex = final streamIndex =
userMediaStreams.indexWhere((stream) => stream.userId == stream.userId); userMediaStreams.indexWhere((stream) => stream.userId == stream.userId);
@ -1145,8 +1148,8 @@ class GroupCall {
onStreamRemoved.add(stream); onStreamRemoved.add(stream);
if (stream.isLocal()) { if (stream.isLocal()) {
stream.disposeRenderer(); await stream.disposeRenderer();
stopMediaStream(stream.stream); await stopMediaStream(stream.stream);
} }
onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged); onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged);
@ -1240,7 +1243,7 @@ class GroupCall {
onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged); onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged);
} }
void removeScreenshareStream(WrappedMediaStream stream) { Future<void> removeScreenshareStream(WrappedMediaStream stream) async {
final streamIndex = screenshareStreams final streamIndex = screenshareStreams
.indexWhere((stream) => stream.userId == stream.userId); .indexWhere((stream) => stream.userId == stream.userId);
@ -1254,8 +1257,8 @@ class GroupCall {
onStreamRemoved.add(stream); onStreamRemoved.add(stream);
if (stream.isLocal()) { if (stream.isLocal()) {
stream.disposeRenderer(); await stream.disposeRenderer();
stopMediaStream(stream.stream); await stopMediaStream(stream.stream);
} }
onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged); onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged);

View File

@ -1,13 +1,24 @@
import 'dart:async'; import 'dart:async';
import 'package:matrix/matrix.dart';
import 'package:random_string/random_string.dart'; import 'package:random_string/random_string.dart';
import 'package:webrtc_interface/webrtc_interface.dart'; import 'package:webrtc_interface/webrtc_interface.dart';
Future<void> stopMediaStream(MediaStream? stream) async { Future<void> stopMediaStream(MediaStream? stream) async {
stream?.getTracks().forEach((element) async { if (stream != null) {
await element.stop(); for (final track in stream.getTracks()) {
}); try {
await stream?.dispose(); 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<MediaStreamTrack> tracks, bool enabled) { void setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) {

View File

@ -14,13 +14,13 @@ abstract class WebRTCDelegate {
Map<String, dynamic> configuration, Map<String, dynamic> configuration,
[Map<String, dynamic> constraints = const {}]); [Map<String, dynamic> constraints = const {}]);
VideoRenderer createRenderer(); VideoRenderer createRenderer();
void playRingtone(); Future<void> playRingtone();
void stopRingtone(); Future<void> stopRingtone();
void handleNewCall(CallSession session); Future<void> handleNewCall(CallSession session);
void handleCallEnded(CallSession session); Future<void> handleCallEnded(CallSession session);
void handleMissedCall(CallSession session); Future<void> handleMissedCall(CallSession session);
void handleNewGroupCall(GroupCall groupCall); Future<void> handleNewGroupCall(GroupCall groupCall);
void handleGroupCallEnded(GroupCall groupCall); Future<void> handleGroupCallEnded(GroupCall groupCall);
bool get isWeb; bool get isWeb;
/// This should be set to false if any calls in the client are in kConnected /// 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)); .listen((event) => _handleEvent(event, onAssertedIdentityReceived));
client.onRoomState.stream.listen( client.onRoomState.stream.listen(
(event) { (event) async {
if ([ if ([
EventTypes.GroupCallPrefix, EventTypes.GroupCallPrefix,
EventTypes.GroupCallMemberPrefix, EventTypes.GroupCallMemberPrefix,
].contains(event.type)) { ].contains(event.type)) {
Logs().v('[VOIP] onRoomState: type ${event.toJson()}.'); Logs().v('[VOIP] onRoomState: type ${event.toJson()}.');
onRoomStateChanged(event); await onRoomStateChanged(event);
} }
}, },
); );
@ -237,7 +237,7 @@ class VoIP {
Logs().v( Logs().v(
'[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.'); '[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.');
await newCall.reject(reason: CallErrorCode.UserBusy, shouldEmit: false); await newCall.reject(reason: CallErrorCode.UserBusy, shouldEmit: false);
delegate.handleMissedCall(newCall); await delegate.handleMissedCall(newCall);
return; return;
} }
@ -254,7 +254,7 @@ class VoIP {
/// Autoplay on firefox still needs interaction, without which all notifications /// Autoplay on firefox still needs interaction, without which all notifications
/// could be blocked. /// could be blocked.
if (confId == null) { if (confId == null) {
delegate.playRingtone(); await delegate.playRingtone();
} }
await newCall.initWithInvite( await newCall.initWithInvite(
@ -264,7 +264,7 @@ class VoIP {
// Popup CallingPage for incoming call. // Popup CallingPage for incoming call.
if (confId == null && !newCall.callHasEnded) { if (confId == null && !newCall.callHasEnded) {
delegate.handleNewCall(newCall); await delegate.handleNewCall(newCall);
} }
onIncomingCall.add(newCall); onIncomingCall.add(newCall);
@ -281,7 +281,7 @@ class VoIP {
if (senderId == client.userID) { if (senderId == client.userID) {
// Ignore messages to yourself. // Ignore messages to yourself.
if (!call.answeredByUs) { if (!call.answeredByUs) {
delegate.stopRingtone(); await delegate.stopRingtone();
} }
if (call.state == CallState.kRinging) { if (call.state == CallState.kRinging) {
call.onAnsweredElsewhere(); call.onAnsweredElsewhere();
@ -333,7 +333,7 @@ class VoIP {
Future<void> onCallHangup(String roomId, String _ /*senderId unused*/, Future<void> onCallHangup(String roomId, String _ /*senderId unused*/,
Map<String, dynamic> content) async { Map<String, dynamic> content) async {
// stop play ringtone, if this is an incoming call // stop play ringtone, if this is an incoming call
delegate.stopRingtone(); await delegate.stopRingtone();
Logs().v('[VOIP] onCallHangup => ${content.toString()}'); Logs().v('[VOIP] onCallHangup => ${content.toString()}');
final String callId = content['call_id']; final String callId = content['call_id'];
final call = calls[callId]; final call = calls[callId];
@ -676,14 +676,16 @@ class VoIP {
Future<void> createGroupCallForRoom(Room room) async { Future<void> createGroupCallForRoom(Room room) async {
final events = await client.getRoomState(room.id); final events = await client.getRoomState(room.id);
events.sort((a, b) => a.originServerTs.compareTo(b.originServerTs)); events.sort((a, b) => a.originServerTs.compareTo(b.originServerTs));
events.forEach((element) async {
if (element.type == EventTypes.GroupCallPrefix) { for (final event in events) {
if (element.content['m.terminated'] != null) { if (event.type == EventTypes.GroupCallPrefix) {
if (event.content['m.terminated'] != null) {
return; return;
} }
await createGroupCallFromRoomStateEvent(element); await createGroupCallFromRoomStateEvent(event);
} }
}); }
return; return;
} }
@ -733,12 +735,12 @@ class VoIP {
onIncomingGroupCall.add(groupCall); onIncomingGroupCall.add(groupCall);
if (emitHandleNewGroupCall) { if (emitHandleNewGroupCall) {
delegate.handleNewGroupCall(groupCall); await delegate.handleNewGroupCall(groupCall);
} }
return groupCall; return groupCall;
} }
void onRoomStateChanged(MatrixEvent event) { Future<void> onRoomStateChanged(MatrixEvent event) async {
final eventType = event.type; final eventType = event.type;
final roomId = event.roomId; final roomId = event.roomId;
if (eventType == EventTypes.GroupCallPrefix) { if (eventType == EventTypes.GroupCallPrefix) {
@ -746,11 +748,11 @@ class VoIP {
final content = event.content; final content = event.content;
final currentGroupCall = groupCalls[groupCallId]; final currentGroupCall = groupCalls[groupCallId];
if (currentGroupCall == null && content['m.terminated'] == null) { if (currentGroupCall == null && content['m.terminated'] == null) {
createGroupCallFromRoomStateEvent(event); await createGroupCallFromRoomStateEvent(event);
} else if (currentGroupCall != null && } else if (currentGroupCall != null &&
currentGroupCall.groupCallId == groupCallId) { currentGroupCall.groupCallId == groupCallId) {
if (content['m.terminated'] != null) { if (content['m.terminated'] != null) {
currentGroupCall.terminate(emitStateEvent: false); await currentGroupCall.terminate(emitStateEvent: false);
} else if (content['m.type'] != currentGroupCall.type) { } else if (content['m.type'] != currentGroupCall.type) {
// TODO: Handle the callType changing when the room state changes // TODO: Handle the callType changing when the room state changes
Logs().w( Logs().w(
@ -767,7 +769,7 @@ class VoIP {
if (groupCall == null) { if (groupCall == null) {
return; return;
} }
groupCall.onMemberStateChanged(event); await groupCall.onMemberStateChanged(event);
} }
} }

View File

@ -87,8 +87,10 @@ extension GroupCallUtils on Room {
final copyGroupCallIds = final copyGroupCallIds =
states.tryGetMap<String, Event>(EventTypes.GroupCallPrefix); states.tryGetMap<String, Event>(EventTypes.GroupCallPrefix);
if (copyGroupCallIds == null) return; if (copyGroupCallIds == null) return;
copyGroupCallIds.forEach( for (final groupCall in copyGroupCallIds.entries) {
(groupCallId, groupCallEvent) async { final groupCallId = groupCall.key;
final groupCallEvent = groupCall.value;
if (groupCallEvent.content.tryGet('m.intent') == 'm.room') return; if (groupCallEvent.content.tryGet('m.intent') == 'm.room') return;
if (!groupCallEvent.content.containsKey('m.terminated')) { if (!groupCallEvent.content.containsKey('m.terminated')) {
Logs().i('found non terminated group call with id $groupCallId'); Logs().i('found non terminated group call with id $groupCallId');
@ -116,8 +118,7 @@ extension GroupCallUtils on Room {
await sendGroupCallTerminateEvent(groupCallId); await sendGroupCallTerminateEvent(groupCallId);
} }
} }
}, }
);
} }
/// returns the event_id if successful /// returns the event_id if successful