Merge branch 'voip/fix-bugs-for-group-call' into 'main'

Fix bugs in 1v1 and group calls.

See merge request famedly/company/frontend/famedlysdk!1132
This commit is contained in:
td 2022-09-18 19:06:30 +00:00
commit 64f2a4b3c6
5 changed files with 53 additions and 40 deletions

View File

@ -82,13 +82,16 @@ class WrappedMediaStream {
Future<void> dispose() async { Future<void> dispose() async {
renderer.srcObject = null; renderer.srcObject = null;
if (isLocal() && stream != null && isWeb) { if (isLocal()) {
for (final element in stream!.getTracks()) { if (isWeb) {
await element.stop(); await stopMediaStream(stream);
} else {
if (!isGroupCall) {
await stopMediaStream(stream);
}
} }
await stream?.dispose();
stream = null;
} }
stream = null;
await renderer.dispose(); await renderer.dispose();
} }
@ -827,7 +830,7 @@ class CallSession {
} }
if (callFeed.purpose == SDPStreamMetadataPurpose.Screenshare) { if (callFeed.purpose == SDPStreamMetadataPurpose.Screenshare) {
stopMediaStream(callFeed.stream); await stopMediaStream(callFeed.stream);
} }
// Empty the array // Empty the array
@ -932,20 +935,19 @@ class CallSession {
/// This used to be done by calling hangup, but is a separate method and protocol /// This used to be done by calling hangup, but is a separate method and protocol
/// event as of MSC2746. /// event as of MSC2746.
/// ///
Future<void> reject() async { Future<void> reject({String? reason, bool shouldEmit = true}) async {
// stop play ringtone // stop play ringtone
voip.delegate.stopRingtone(); voip.delegate.stopRingtone();
if (state != CallState.kRinging && state != CallState.kFledgling) {
if (state != CallState.kRinging) { Logs().e('[VOIP] Call must be in \'ringing|fledgling\' state to reject!');
Logs().e('[VOIP] Call must be in \'ringing\' state to reject!');
return; return;
} }
Logs().d('[VOIP] Rejecting call: $callId'); Logs().d('[VOIP] Rejecting call: $callId');
await terminate(CallParty.kLocal, CallErrorCode.UserHangup, true); await terminate(CallParty.kLocal, CallErrorCode.UserHangup, shouldEmit);
await sendCallReject(room, callId, lifetimeMs, localPartyId); await sendCallReject(room, callId, lifetimeMs, localPartyId, reason);
} }
Future<void> hangup([String? reason, bool suppressEvent = true]) async { Future<void> hangup([String? reason, bool suppressEvent = false]) async {
// stop play ringtone // stop play ringtone
voip.delegate.stopRingtone(); voip.delegate.stopRingtone();
@ -987,15 +989,15 @@ class CallSession {
hangupParty = party; hangupParty = party;
hangupReason = reason; hangupReason = reason;
setCallState(CallState.kEnded); if (shouldEmit) {
setCallState(CallState.kEnded);
}
voip.currentCID = null; voip.currentCID = null;
voip.calls.remove(callId); voip.calls.remove(callId);
await cleanUp(); await cleanUp();
onCallHangup.add(this);
voip.delegate.handleCallEnded(this);
if (shouldEmit) { if (shouldEmit) {
onCallHangup.add(this);
voip.delegate.handleCallEnded(this);
fireCallEvent(CallEvent.kHangup); fireCallEvent(CallEvent.kHangup);
} }
} }
@ -1432,7 +1434,7 @@ class CallSession {
/// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1.
/// [party_id] The party ID for call, Can be set to client.deviceId. /// [party_id] The party ID for call, Can be set to client.deviceId.
Future<String?> sendCallReject( Future<String?> sendCallReject(
Room room, String callId, int lifetime, String party_id, Room room, String callId, int lifetime, String party_id, String? reason,
{String version = voipProtoVersion, String? txid}) async { {String version = voipProtoVersion, String? txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
@ -1440,6 +1442,7 @@ class CallSession {
'call_id': callId, 'call_id': callId,
'party_id': party_id, 'party_id': party_id,
if (groupCallId != null) 'conf_id': groupCallId, if (groupCallId != null) 'conf_id': groupCallId,
if (reason != null) 'reason': reason,
'version': version, 'version': version,
'lifetime': lifetime, 'lifetime': lifetime,
}; };

View File

@ -594,9 +594,10 @@ class GroupCall {
onGroupCallEvent.add(GroupCallEvent.LocalScreenshareStateChanged); onGroupCallEvent.add(GroupCallEvent.LocalScreenshareStateChanged);
calls.forEach((call) { calls.forEach((call) async {
call.addLocalStream( await call.addLocalStream(
localScreenshareStream!.stream!, localScreenshareStream!.purpose); await voip.delegate.cloneStream(localScreenshareStream!.stream!),
localScreenshareStream!.purpose);
}); });
await sendMemberStateEvent(); await sendMemberStateEvent();
@ -613,7 +614,7 @@ class GroupCall {
calls.forEach((call) { calls.forEach((call) {
call.removeLocalStream(call.localScreenSharingStream!); call.removeLocalStream(call.localScreenSharingStream!);
}); });
stopMediaStream(localScreenshareStream!.stream); await stopMediaStream(localScreenshareStream?.stream);
removeScreenshareStream(localScreenshareStream!); removeScreenshareStream(localScreenshareStream!);
localScreenshareStream = null; localScreenshareStream = null;
localDesktopCapturerSourceId = null; localDesktopCapturerSourceId = null;
@ -1092,12 +1093,10 @@ class GroupCall {
onStreamRemoved.add(stream); onStreamRemoved.add(stream);
stream.stream!.getTracks().forEach((element) { if (stream.isLocal()) {
element.stop(); stream.disposeRenderer();
}); stopMediaStream(stream.stream);
}
stream.stream!.dispose();
stream.disposeRenderer();
onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged); onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged);
@ -1187,14 +1186,11 @@ class GroupCall {
onStreamRemoved.add(stream); onStreamRemoved.add(stream);
if (voip.delegate.isWeb) { if (stream.isLocal()) {
stream.stream!.getTracks().forEach((element) { stream.disposeRenderer();
element.stop(); stopMediaStream(stream.stream);
});
} }
stream.dispose();
onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged); onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged);
} }

View File

@ -1,10 +1,11 @@
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';
void stopMediaStream(MediaStream? stream) async { Future<void> stopMediaStream(MediaStream? stream) async {
stream?.getTracks().forEach((element) async { stream?.getTracks().forEach((element) async {
await element.stop(); await element.stop();
}); });
await stream?.dispose();
} }
void setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) { void setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) {

View File

@ -19,12 +19,12 @@ abstract class WebRTCDelegate {
void stopRingtone(); void stopRingtone();
void handleNewCall(CallSession session); void handleNewCall(CallSession session);
void handleCallEnded(CallSession session); void handleCallEnded(CallSession session);
void handleMissedCall(CallSession session);
void handleNewGroupCall(GroupCall groupCall); void handleNewGroupCall(GroupCall groupCall);
void handleGroupCallEnded(GroupCall groupCall); void handleGroupCallEnded(GroupCall groupCall);
bool get isBackgroud; bool get isBackgroud;
bool get isWeb; bool get isWeb;
bool get canHandleNewCall => true;
} }
class VoIP { class VoIP {
@ -200,10 +200,20 @@ class VoIP {
newCall.opponentDeviceId = deviceId; newCall.opponentDeviceId = deviceId;
newCall.opponentSessionId = content['sender_session_id']; newCall.opponentSessionId = content['sender_session_id'];
if (!delegate.canHandleNewCall &&
(confId == null || confId != currentGroupCID)) {
Logs().v(
'[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.');
await newCall.reject(reason: 'busy', shouldEmit: false);
delegate.handleMissedCall(newCall);
return;
}
final offer = RTCSessionDescription( final offer = RTCSessionDescription(
content['offer']['sdp'], content['offer']['sdp'],
content['offer']['type'], content['offer']['type'],
); );
await newCall.initWithInvite( await newCall.initWithInvite(
callType, offer, sdpStreamMetadata, lifetime, confId != null); callType, offer, sdpStreamMetadata, lifetime, confId != null);
@ -223,7 +233,9 @@ class VoIP {
///TODO: notify the callkeep that the call is incoming. ///TODO: notify the callkeep that the call is incoming.
} }
// Play ringtone // Play ringtone
delegate.playRingtone(); if (confId == null) {
delegate.playRingtone();
}
} }
Future<void> onCallAnswer( Future<void> onCallAnswer(

View File

@ -967,7 +967,8 @@ void main() {
await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234'); await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234');
await call.sendSelectCallAnswer(room, '1234', 1234, '4567', '6789', await call.sendSelectCallAnswer(room, '1234', 1234, '4567', '6789',
txid: '1234'); txid: '1234');
await call.sendCallReject(room, '1234', 1234, '4567', txid: '1234'); await call.sendCallReject(room, '1234', 1234, '4567', 'busy',
txid: '1234');
await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp', await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp',
txid: '1234'); txid: '1234');
await call.sendHangupCall(room, '1234', '4567', 'user_hangup', await call.sendHangupCall(room, '1234', '4567', 'user_hangup',