Merge branch 'td/asyncmesh' into 'main'
fix: make group call stuff async, let clients await what they need See merge request famedly/company/frontend/famedlysdk!1244
This commit is contained in:
commit
b776e8f9df
|
|
@ -508,10 +508,10 @@ class CallSession {
|
|||
});
|
||||
}
|
||||
|
||||
void answerWithStreams(List<WrappedMediaStream> callFeeds) {
|
||||
Future<void> answerWithStreams(List<WrappedMediaStream> callFeeds) async {
|
||||
if (inviteOrAnswerSent) return;
|
||||
Logs().d('nswering call $callId');
|
||||
gotCallFeedsForAnswer(callFeeds);
|
||||
await gotCallFeedsForAnswer(callFeeds);
|
||||
}
|
||||
|
||||
void replacedBy(CallSession newCall) {
|
||||
|
|
@ -782,13 +782,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) {
|
||||
|
|
@ -1082,7 +1083,7 @@ class CallSession {
|
|||
return;
|
||||
}
|
||||
// stop play ringtone
|
||||
voip.delegate.stopRingtone();
|
||||
await voip.delegate.stopRingtone();
|
||||
|
||||
if (direction == CallDirection.kIncoming) {
|
||||
setCallState(CallState.kCreateAnswer);
|
||||
|
|
@ -1132,7 +1133,7 @@ class CallSession {
|
|||
///
|
||||
Future<void> 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;
|
||||
|
|
@ -1147,7 +1148,7 @@ class CallSession {
|
|||
|
||||
Future<void> 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);
|
||||
|
|
@ -1185,7 +1186,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);
|
||||
|
|
@ -1209,10 +1210,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1402,9 +1403,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(
|
||||
|
|
@ -1420,10 +1421,12 @@ class CallSession {
|
|||
SDPStreamMetadata _getLocalSDPStreamMetadata() {
|
||||
final sdpStreamMetadatas = <String, SDPStreamPurpose>{};
|
||||
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()}');
|
||||
|
|
@ -1482,18 +1485,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;
|
||||
|
|
|
|||
|
|
@ -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<void> 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<GroupCall>(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<void> 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<void> sendMemberStateEvent() async {
|
||||
|
|
@ -782,7 +783,7 @@ class GroupCall {
|
|||
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.
|
||||
if (event.roomId != room.id) {
|
||||
return;
|
||||
|
|
@ -892,7 +893,7 @@ class GroupCall {
|
|||
await newCall.placeCallWithStreams(
|
||||
getLocalStreams(), requestScreenshareFeed);
|
||||
|
||||
addCall(newCall);
|
||||
await addCall(newCall);
|
||||
}
|
||||
|
||||
Future<IGroupCallRoomMemberDevice?> getDeviceForMember(String userId) async {
|
||||
|
|
@ -929,13 +930,14 @@ class GroupCall {
|
|||
return null;
|
||||
}
|
||||
|
||||
void addCall(CallSession call) {
|
||||
Future<void> addCall(CallSession call) async {
|
||||
calls.add(call);
|
||||
initCall(call);
|
||||
await initCall(call);
|
||||
onGroupCallEvent.add(GroupCallEvent.CallsChanged);
|
||||
}
|
||||
|
||||
void replaceCall(CallSession existingCall, CallSession replacementCall) {
|
||||
Future<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,23 @@ import 'dart:async';
|
|||
import 'package:random_string/random_string.dart';
|
||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
Future<void> 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<MediaStreamTrack> tracks, bool enabled) {
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ abstract class WebRTCDelegate {
|
|||
Map<String, dynamic> configuration,
|
||||
[Map<String, dynamic> 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<void> playRingtone();
|
||||
Future<void> stopRingtone();
|
||||
Future<void> handleNewCall(CallSession session);
|
||||
Future<void> handleCallEnded(CallSession session);
|
||||
Future<void> handleMissedCall(CallSession session);
|
||||
Future<void> handleNewGroupCall(GroupCall groupCall);
|
||||
Future<void> 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<void> onCallHangup(String roomId, String _ /*senderId unused*/,
|
||||
Map<String, dynamic> 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<void> 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<void> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,37 +89,38 @@ extension GroupCallUtils on Room {
|
|||
final copyGroupCallIds =
|
||||
states.tryGetMap<String, Event>(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<String, Event>(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<String, Event>(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
|
||||
|
|
|
|||
Loading…
Reference in New Issue