From b203b5dacbdbc34eb88465a35fbef1dfd7b661f0 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Tue, 13 Sep 2022 10:34:07 +0800 Subject: [PATCH 1/7] fix: Fix remote hangup call causing local screenstream to be released. --- lib/src/voip/group_call.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/voip/group_call.dart b/lib/src/voip/group_call.dart index df9d1fa6..27fb8c98 100644 --- a/lib/src/voip/group_call.dart +++ b/lib/src/voip/group_call.dart @@ -594,9 +594,10 @@ class GroupCall { onGroupCallEvent.add(GroupCallEvent.LocalScreenshareStateChanged); - calls.forEach((call) { - call.addLocalStream( - localScreenshareStream!.stream!, localScreenshareStream!.purpose); + calls.forEach((call) async { + await call.addLocalStream( + await voip.delegate.cloneStream(localScreenshareStream!.stream!), + localScreenshareStream!.purpose); }); await sendMemberStateEvent(); From 62cca99edd142e0bd9b182e5d4a74c583908088b Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Wed, 14 Sep 2022 23:07:32 +0800 Subject: [PATCH 2/7] fix: Ensures that p2p/group calls are in progress to reject new call invitations, and emits a call reject events. --- lib/src/voip/call.dart | 24 ++++++++++++------------ lib/src/voip/voip.dart | 18 +++++++++++++++--- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/lib/src/voip/call.dart b/lib/src/voip/call.dart index f6ec3f0a..6f6cf3ad 100644 --- a/lib/src/voip/call.dart +++ b/lib/src/voip/call.dart @@ -932,17 +932,16 @@ class CallSession { /// This used to be done by calling hangup, but is a separate method and protocol /// event as of MSC2746. /// - Future reject() async { + Future reject({String? reason, bool shouldEmit = true}) async { // stop play ringtone voip.delegate.stopRingtone(); - - if (state != CallState.kRinging) { - Logs().e('[VOIP] Call must be in \'ringing\' state to reject!'); + if (state != CallState.kRinging && state != CallState.kFledgling) { + Logs().e('[VOIP] Call must be in \'ringing|fledgling\' state to reject!'); return; } Logs().d('[VOIP] Rejecting call: $callId'); - await terminate(CallParty.kLocal, CallErrorCode.UserHangup, true); - await sendCallReject(room, callId, lifetimeMs, localPartyId); + await terminate(CallParty.kLocal, CallErrorCode.UserHangup, shouldEmit); + await sendCallReject(room, callId, lifetimeMs, localPartyId, reason); } Future hangup([String? reason, bool suppressEvent = true]) async { @@ -987,15 +986,15 @@ class CallSession { hangupParty = party; hangupReason = reason; - setCallState(CallState.kEnded); + if (shouldEmit) { + setCallState(CallState.kEnded); + } voip.currentCID = null; voip.calls.remove(callId); await cleanUp(); - - onCallHangup.add(this); - - voip.delegate.handleCallEnded(this); if (shouldEmit) { + onCallHangup.add(this); + voip.delegate.handleCallEnded(this); fireCallEvent(CallEvent.kHangup); } } @@ -1432,7 +1431,7 @@ class CallSession { /// [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. Future 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 { txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; @@ -1440,6 +1439,7 @@ class CallSession { 'call_id': callId, 'party_id': party_id, if (groupCallId != null) 'conf_id': groupCallId, + if (reason != null) 'reason': reason, 'version': version, 'lifetime': lifetime, }; diff --git a/lib/src/voip/voip.dart b/lib/src/voip/voip.dart index d9e69a08..0e9bf88e 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -19,12 +19,12 @@ abstract class WebRTCDelegate { void stopRingtone(); void handleNewCall(CallSession session); void handleCallEnded(CallSession session); - + void handleMissedCall(CallSession session); void handleNewGroupCall(GroupCall groupCall); void handleGroupCallEnded(GroupCall groupCall); - bool get isBackgroud; bool get isWeb; + bool get canHandleNewCall => true; } class VoIP { @@ -200,10 +200,20 @@ class VoIP { newCall.opponentDeviceId = deviceId; newCall.opponentSessionId = content['sender_session_id']; + if (!delegate.canHandleNewCall && + (confId == null || confId != currentGroupCID)) { + Logs().v( + '[VOIP] onCallInvite: canRespondNewCalls is false, maybe user is busy.'); + await newCall.reject(reason: 'busy', shouldEmit: false); + delegate.handleMissedCall(newCall); + return; + } + final offer = RTCSessionDescription( content['offer']['sdp'], content['offer']['type'], ); + await newCall.initWithInvite( callType, offer, sdpStreamMetadata, lifetime, confId != null); @@ -223,7 +233,9 @@ class VoIP { ///TODO: notify the callkeep that the call is incoming. } // Play ringtone - delegate.playRingtone(); + if (confId == null) { + delegate.playRingtone(); + } } Future onCallAnswer( From 7e1abd2b957559644686f0b870f2c198011d4512 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Wed, 14 Sep 2022 23:48:29 +0800 Subject: [PATCH 3/7] chore: Fire events by default during hangup. --- lib/src/voip/call.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/voip/call.dart b/lib/src/voip/call.dart index 6f6cf3ad..5b83a731 100644 --- a/lib/src/voip/call.dart +++ b/lib/src/voip/call.dart @@ -944,7 +944,7 @@ class CallSession { await sendCallReject(room, callId, lifetimeMs, localPartyId, reason); } - Future hangup([String? reason, bool suppressEvent = true]) async { + Future hangup([String? reason, bool suppressEvent = false]) async { // stop play ringtone voip.delegate.stopRingtone(); From 4a5cf7d55df635d61aeaac6c013ab5875a851f9f Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Fri, 16 Sep 2022 00:04:22 +0800 Subject: [PATCH 4/7] chore: update. --- lib/src/voip/call.dart | 13 ++++++++----- lib/src/voip/group_call.dart | 16 ++++------------ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/src/voip/call.dart b/lib/src/voip/call.dart index 5b83a731..326cb9f3 100644 --- a/lib/src/voip/call.dart +++ b/lib/src/voip/call.dart @@ -82,13 +82,16 @@ class WrappedMediaStream { Future dispose() async { renderer.srcObject = null; - if (isLocal() && stream != null && isWeb) { - for (final element in stream!.getTracks()) { - await element.stop(); + if (isLocal()) { + if (isWeb) { + await stream?.dispose(); + } else { + if (!isGroupCall) { + await stream?.dispose(); + } } - await stream?.dispose(); - stream = null; } + stream = null; await renderer.dispose(); } diff --git a/lib/src/voip/group_call.dart b/lib/src/voip/group_call.dart index 27fb8c98..f0ab7703 100644 --- a/lib/src/voip/group_call.dart +++ b/lib/src/voip/group_call.dart @@ -1093,12 +1093,10 @@ class GroupCall { onStreamRemoved.add(stream); - stream.stream!.getTracks().forEach((element) { - element.stop(); - }); - - stream.stream!.dispose(); - stream.disposeRenderer(); + if (stream.isLocal()) { + stream?.disposeRenderer(); + stream?.stream?.dispose(); + } onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged); @@ -1188,12 +1186,6 @@ class GroupCall { onStreamRemoved.add(stream); - if (voip.delegate.isWeb) { - stream.stream!.getTracks().forEach((element) { - element.stop(); - }); - } - stream.dispose(); onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged); From df426e763ac3e5abf9e79c4776a533e6dd7a30df Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Fri, 16 Sep 2022 23:29:49 +0800 Subject: [PATCH 5/7] chore: Properly close usermedia/screen stream for 1v1/group calls. --- lib/src/voip/call.dart | 4 ++-- lib/src/voip/group_call.dart | 11 +++++++---- lib/src/voip/utils.dart | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/src/voip/call.dart b/lib/src/voip/call.dart index 326cb9f3..9437ef7c 100644 --- a/lib/src/voip/call.dart +++ b/lib/src/voip/call.dart @@ -84,10 +84,10 @@ class WrappedMediaStream { renderer.srcObject = null; if (isLocal()) { if (isWeb) { - await stream?.dispose(); + await stopMediaStream(stream); } else { if (!isGroupCall) { - await stream?.dispose(); + await stopMediaStream(stream); } } } diff --git a/lib/src/voip/group_call.dart b/lib/src/voip/group_call.dart index f0ab7703..b4b8ef46 100644 --- a/lib/src/voip/group_call.dart +++ b/lib/src/voip/group_call.dart @@ -614,7 +614,7 @@ class GroupCall { calls.forEach((call) { call.removeLocalStream(call.localScreenSharingStream!); }); - stopMediaStream(localScreenshareStream!.stream); + await stopMediaStream(localScreenshareStream?.stream); removeScreenshareStream(localScreenshareStream!); localScreenshareStream = null; localDesktopCapturerSourceId = null; @@ -1094,8 +1094,8 @@ class GroupCall { onStreamRemoved.add(stream); if (stream.isLocal()) { - stream?.disposeRenderer(); - stream?.stream?.dispose(); + stream.disposeRenderer(); + stopMediaStream(stream.stream); } onGroupCallEvent.add(GroupCallEvent.UserMediaStreamsChanged); @@ -1186,7 +1186,10 @@ class GroupCall { onStreamRemoved.add(stream); - stream.dispose(); + if (stream.isLocal()) { + stream.disposeRenderer(); + stopMediaStream(stream.stream); + } onGroupCallEvent.add(GroupCallEvent.ScreenshareStreamsChanged); } diff --git a/lib/src/voip/utils.dart b/lib/src/voip/utils.dart index c331d104..26dfc9ec 100644 --- a/lib/src/voip/utils.dart +++ b/lib/src/voip/utils.dart @@ -1,10 +1,11 @@ import 'package:random_string/random_string.dart'; import 'package:webrtc_interface/webrtc_interface.dart'; -void stopMediaStream(MediaStream? stream) async { +Future stopMediaStream(MediaStream? stream) async { stream?.getTracks().forEach((element) async { await element.stop(); }); + await stream?.dispose(); } void setTracksEnabled(List tracks, bool enabled) { From e1ccda3cb13a0432010ff8f5f0fca187568d399b Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Sat, 17 Sep 2022 00:10:50 +0800 Subject: [PATCH 6/7] chore: update. --- lib/src/voip/voip.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/voip/voip.dart b/lib/src/voip/voip.dart index 0e9bf88e..f48e30e6 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -203,7 +203,7 @@ class VoIP { if (!delegate.canHandleNewCall && (confId == null || confId != currentGroupCID)) { Logs().v( - '[VOIP] onCallInvite: canRespondNewCalls is false, maybe user is busy.'); + '[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.'); await newCall.reject(reason: 'busy', shouldEmit: false); delegate.handleMissedCall(newCall); return; From 62cd4e78fbecc0075a7d6430739baa8e4fc71393 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Sat, 17 Sep 2022 00:19:21 +0800 Subject: [PATCH 7/7] chore: fix analyzer error. --- lib/src/voip/call.dart | 2 +- test/room_test.dart | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/voip/call.dart b/lib/src/voip/call.dart index 9437ef7c..18949c4d 100644 --- a/lib/src/voip/call.dart +++ b/lib/src/voip/call.dart @@ -830,7 +830,7 @@ class CallSession { } if (callFeed.purpose == SDPStreamMetadataPurpose.Screenshare) { - stopMediaStream(callFeed.stream); + await stopMediaStream(callFeed.stream); } // Empty the array diff --git a/test/room_test.dart b/test/room_test.dart index a9149bcc..8fe18f5a 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -967,7 +967,8 @@ void main() { await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234'); await call.sendSelectCallAnswer(room, '1234', 1234, '4567', '6789', 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', txid: '1234'); await call.sendHangupCall(room, '1234', '4567', 'user_hangup',