diff --git a/Session/Calls/Call Management/SessionCall.swift b/Session/Calls/Call Management/SessionCall.swift index ce24cc842..f533291ea 100644 --- a/Session/Calls/Call Management/SessionCall.swift +++ b/Session/Calls/Call Management/SessionCall.swift @@ -13,7 +13,6 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { let isOutgoing: Bool var remoteSDP: RTCSessionDescription? = nil var callMessageTimestamp: UInt64? - var isWaitingForRemoteSDP = false var answerCallAction: CXAnswerCallAction? = nil var contactName: String { let contact = Storage.shared.getContact(with: self.sessionID) @@ -157,11 +156,9 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { } func didReceiveRemoteSDP(sdp: RTCSessionDescription) { - guard remoteSDP == nil else { return } remoteSDP = sdp - if isWaitingForRemoteSDP { + if hasStartedConnecting { webRTCSession.handleRemoteSDP(sdp, from: sessionID) // This sends an answer message internally - isWaitingForRemoteSDP = false } } @@ -186,8 +183,6 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { hasStartedConnecting = true if let sdp = remoteSDP { webRTCSession.handleRemoteSDP(sdp, from: sessionID) // This sends an answer message internally - } else { - isWaitingForRemoteSDP = true } } @@ -222,12 +217,18 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { let durationString = NSString.formatDurationSeconds(UInt32(self.duration), useShortFormat: true) newMessageBody = "\(self.isOutgoing ? NSLocalizedString("call_outgoing", comment: "") : NSLocalizedString("call_incoming", comment: "")): \(durationString)" shouldMarkAsRead = true + } else if self.hasStartedConnecting { + newMessageBody = NSLocalizedString("call_cancelled", comment: "") + shouldMarkAsRead = true } else { switch mode { case .local: newMessageBody = self.isOutgoing ? NSLocalizedString("call_cancelled", comment: "") : NSLocalizedString("call_rejected", comment: "") shouldMarkAsRead = true case .remote: + if self.hasStartedConnecting { + + } newMessageBody = self.isOutgoing ? NSLocalizedString("call_rejected", comment: "") : NSLocalizedString("call_missing", comment: "") } } @@ -254,6 +255,7 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { // MARK: Delegate public func webRTCIsConnected() { + guard !self.hasConnected else { return } self.hasConnected = true self.answerCallAction?.fulfill() } @@ -263,6 +265,7 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { } public func didReceiveHangUpSignal() { + self.hasEnded = true DispatchQueue.main.async { if let currentBanner = IncomingCallBanner.current { currentBanner.dismiss() } if let callVC = CurrentAppContext().frontmostViewController() as? CallVC { callVC.handleEndCallMessage() } diff --git a/Session/Calls/CallVC.swift b/Session/Calls/CallVC.swift index af8b80e00..e9f3defcb 100644 --- a/Session/Calls/CallVC.swift +++ b/Session/Calls/CallVC.swift @@ -193,8 +193,7 @@ final class CallVC : UIViewController, VideoPreviewDelegate { } self.call.hasEndedDidChange = { DispatchQueue.main.async { - self.conversationVC?.showInputAccessoryView() - self.presentingViewController?.dismiss(animated: true, completion: nil) + self.handleEndCallMessage() } } } @@ -331,6 +330,10 @@ final class CallVC : UIViewController, VideoPreviewDelegate { self.call.endSessionCall() AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: nil) } + DispatchQueue.main.async { + self.conversationVC?.showInputAccessoryView() + self.presentingViewController?.dismiss(animated: true, completion: nil) + } } } diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 038a7d977..f7e64c8dc 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -25,7 +25,7 @@ extension AppDelegate { MessageReceiver.handlePreOfferCallMessage = { (message, transaction) in guard CurrentAppContext().isMainApp else { return } let callManager = AppEnvironment.shared.callManager - guard callManager.currentCall == nil || !SSKPreferences.areCallsEnabled else { + guard callManager.currentCall == nil && SSKPreferences.areCallsEnabled else { callManager.handleIncomingCallOfferInBusyOrUnenabledState(offerMessage: message, using: transaction) return } @@ -65,6 +65,10 @@ extension AppDelegate { // Answer messages MessageReceiver.handleAnswerCallMessage = { message in DispatchQueue.main.async { + guard let call = AppEnvironment.shared.callManager.currentCall, message.uuid == call.uuid.uuidString else { return } + call.hasStartedConnecting = true + let sdp = RTCSessionDescription(type: .answer, sdp: message.sdps![0]) + call.didReceiveRemoteSDP(sdp: sdp) guard let callVC = CurrentAppContext().frontmostViewController() as? CallVC else { return } callVC.handleAnswerMessage(message) } diff --git a/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift b/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift index 7ad8413d4..0ab2fa2d6 100644 --- a/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift +++ b/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift @@ -13,8 +13,7 @@ extension WebRTCSession { if let error = error { SNLog("[Calls] Couldn't set SDP due to error: \(error).") } else { - guard let self = self, - sdp.type == .offer, self.peerConnection.localDescription == nil else { return } + guard let self = self, sdp.type == .offer else { return } Storage.write { transaction in self.sendAnswer(to: sessionID, using: transaction).retainUntilComplete() } diff --git a/SessionMessagingKit/Calls/WebRTCSession.swift b/SessionMessagingKit/Calls/WebRTCSession.swift index 93e4e15e9..d6e954f14 100644 --- a/SessionMessagingKit/Calls/WebRTCSession.swift +++ b/SessionMessagingKit/Calls/WebRTCSession.swift @@ -41,15 +41,6 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { return factory.peerConnection(with: configuration, constraints: constraints, delegate: self) }() - internal lazy var mediaConstraints: RTCMediaConstraints = { - let mandatory: [String:String] = [ - kRTCMediaConstraintsOfferToReceiveAudio : kRTCMediaConstraintsValueTrue, - kRTCMediaConstraintsOfferToReceiveVideo : kRTCMediaConstraintsValueTrue, - ] - let optional: [String:String] = [:] - return RTCMediaConstraints(mandatoryConstraints: mandatory, optionalConstraints: optional) - }() - // Audio internal lazy var audioSource: RTCAudioSource = { let constraints = RTCMediaConstraints(mandatoryConstraints: [:], optionalConstraints: [:]) @@ -110,6 +101,16 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { dataChannel.delegate = self self.localDataChannel = dataChannel } + + // Network reachability + NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { _ in + print("[Calls] Reachability did change.") + if self.peerConnection.signalingState == .stable { + Storage.write { transaction in + self.sendOffer(to: self.contactSessionID, using: transaction, isRestartingICEConnection: true).retainUntilComplete() + } + } + } } // MARK: Signaling @@ -133,11 +134,11 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { return promise } - public func sendOffer(to sessionID: String, using transaction: YapDatabaseReadWriteTransaction) -> Promise { + public func sendOffer(to sessionID: String, using transaction: YapDatabaseReadWriteTransaction, isRestartingICEConnection: Bool = false) -> Promise { print("[Calls] Sending offer message.") guard let thread = TSContactThread.fetch(for: sessionID, using: transaction) else { return Promise(error: Error.noThread) } let (promise, seal) = Promise.pending() - peerConnection.offer(for: mediaConstraints) { [weak self] sdp, error in + peerConnection.offer(for: mediaConstraints(isRestartingICEConnection)) { [weak self] sdp, error in if let error = error { seal.reject(error) } else { @@ -169,7 +170,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { print("[Calls] Sending answer message.") guard let thread = TSContactThread.fetch(for: sessionID, using: transaction) else { return Promise(error: Error.noThread) } let (promise, seal) = Promise.pending() - peerConnection.answer(for: mediaConstraints) { [weak self] sdp, error in + peerConnection.answer(for: mediaConstraints(false)) { [weak self] sdp, error in if let error = error { seal.reject(error) } else { @@ -236,6 +237,16 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { peerConnection.close() } + private func mediaConstraints(_ isRestartingICEConnection: Bool) -> RTCMediaConstraints { + var mandatory: [String:String] = [ + kRTCMediaConstraintsOfferToReceiveAudio : kRTCMediaConstraintsValueTrue, + kRTCMediaConstraintsOfferToReceiveVideo : kRTCMediaConstraintsValueTrue, + ] + if isRestartingICEConnection { mandatory[kRTCMediaConstraintsIceRestart] = kRTCMediaConstraintsValueTrue } + let optional: [String:String] = [:] + return RTCMediaConstraints(mandatoryConstraints: mandatory, optionalConstraints: optional) + } + // MARK: Peer connection delegate public func peerConnection(_ peerConnection: RTCPeerConnection, didChange state: RTCSignalingState) { print("[Calls] Signaling state changed to: \(state).") diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 0caa30142..4db78aa44 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -287,8 +287,6 @@ extension MessageReceiver { case .answer: print("[Calls] Received answer message.") guard let currentWebRTCSession = WebRTCSession.current, currentWebRTCSession.uuid == message.uuid! else { return } - let sdp = RTCSessionDescription(type: .answer, sdp: message.sdps![0]) - currentWebRTCSession.handleRemoteSDP(sdp, from: message.sender!) handleAnswerCallMessage?(message) case .provisionalAnswer: break // TODO: Implement case let .iceCandidates(sdpMLineIndexes, sdpMids):