Merge branch 'charlesmchen/callLifecycleEdgeCases'

pull/1/head
Matthew Chen 7 years ago
commit d3d4157c2f

@ -98,6 +98,108 @@ protocol CallServiceObserver: class {
remoteVideoTrack: RTCVideoTrack?)
}
// Gather all per-call state in one place.
private class SignalCallData: NSObject {
public let call: SignalCall
// Used to coordinate promises across delegate methods
let fulfillCallConnectedPromise: (() -> Void)
let rejectCallConnectedPromise: ((Error) -> Void)
let callConnectedPromise: Promise<Void>
// Used to ensure any received ICE messages wait until the peer connection client is set up.
let fulfillPeerConnectionClientPromise: (() -> Void)
let rejectPeerConnectionClientPromise: ((Error) -> Void)
let peerConnectionClientPromise: Promise<Void>
// Used to ensure CallOffer was sent before sending any ICE updates.
let fulfillReadyToSendIceUpdatesPromise: (() -> Void)
let rejectReadyToSendIceUpdatesPromise: ((Error) -> Void)
let readyToSendIceUpdatesPromise: Promise<Void>
weak var localVideoTrack: RTCVideoTrack? {
didSet {
SwiftAssertIsOnMainThread(#function)
Logger.info("\(self.logTag) \(#function)")
}
}
weak var remoteVideoTrack: RTCVideoTrack? {
didSet {
SwiftAssertIsOnMainThread(#function)
Logger.info("\(self.logTag) \(#function)")
}
}
var isRemoteVideoEnabled = false {
didSet {
SwiftAssertIsOnMainThread(#function)
Logger.info("\(self.logTag) \(#function): \(isRemoteVideoEnabled)")
}
}
var peerConnectionClient: PeerConnectionClient? {
didSet {
SwiftAssertIsOnMainThread(#function)
Logger.debug("\(self.logTag) .peerConnectionClient setter: \(oldValue != nil) -> \(peerConnectionClient != nil) \(String(describing: peerConnectionClient))")
}
}
required init(call: SignalCall) {
self.call = call
let (callConnectedPromise, fulfillCallConnectedPromise, rejectCallConnectedPromise) = Promise<Void>.pending()
self.callConnectedPromise = callConnectedPromise
self.fulfillCallConnectedPromise = fulfillCallConnectedPromise
self.rejectCallConnectedPromise = rejectCallConnectedPromise
let (peerConnectionClientPromise, fulfillPeerConnectionClientPromise, rejectPeerConnectionClientPromise) = Promise<Void>.pending()
self.peerConnectionClientPromise = peerConnectionClientPromise
self.fulfillPeerConnectionClientPromise = fulfillPeerConnectionClientPromise
self.rejectPeerConnectionClientPromise = rejectPeerConnectionClientPromise
let (readyToSendIceUpdatesPromise, fulfillReadyToSendIceUpdatesPromise, rejectReadyToSendIceUpdatesPromise) = Promise<Void>.pending()
self.readyToSendIceUpdatesPromise = readyToSendIceUpdatesPromise
self.fulfillReadyToSendIceUpdatesPromise = fulfillReadyToSendIceUpdatesPromise
self.rejectReadyToSendIceUpdatesPromise = rejectReadyToSendIceUpdatesPromise
super.init()
}
deinit {
Logger.debug("[SignalCallData] deinit")
}
// MARK: -
public func terminate() {
SwiftAssertIsOnMainThread(#function)
Logger.debug("\(self.logTag) in \(#function)")
self.call.removeAllObservers()
// In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak.
// There is no harm in rejecting a previously fulfilled promise.
rejectCallConnectedPromise(CallError.obsoleteCall(description: "Terminating call"))
// In case we're still waiting on the peer connection setup somewhere, we need to reject it to avoid a memory leak.
// There is no harm in rejecting a previously fulfilled promise.
rejectPeerConnectionClientPromise(CallError.obsoleteCall(description: "Terminating call"))
// In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak.
// There is no harm in rejecting a previously fulfilled promise.
rejectReadyToSendIceUpdatesPromise(CallError.obsoleteCall(description: "Terminating call"))
peerConnectionClient?.terminate()
Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function)")
}
}
// This class' state should only be accessed on the main queue.
@objc class CallService: NSObject, CallObserver, PeerConnectionClientDelegate {
@ -122,85 +224,71 @@ protocol CallServiceObserver: class {
// MARK: Ivars
var peerConnectionClient: PeerConnectionClient? {
fileprivate var callData: SignalCallData? {
didSet {
SwiftAssertIsOnMainThread(#function)
Logger.debug("\(self.logTag) .peerConnectionClient setter: \(oldValue != nil) -> \(peerConnectionClient != nil) \(String(describing: peerConnectionClient))")
}
}
var call: SignalCall? {
didSet {
SwiftAssertIsOnMainThread(#function)
oldValue?.removeObserver(self)
call?.addObserverAndSyncState(observer: self)
oldValue?.call.removeObserver(self)
callData?.call.addObserverAndSyncState(observer: self)
updateIsVideoEnabled()
// Prevent device from sleeping while we have an active call.
if oldValue != call {
if oldValue != callData {
if let oldValue = oldValue {
DeviceSleepManager.sharedInstance.removeBlock(blockObject: oldValue)
}
stopAnyCallTimer()
if let call = call {
DeviceSleepManager.sharedInstance.addBlock(blockObject: call)
if let callData = callData {
DeviceSleepManager.sharedInstance.addBlock(blockObject: callData)
self.startCallTimer()
}
}
Logger.debug("\(self.logTag) .call setter: \(oldValue?.identifiersForLogs as Optional) -> \(call?.identifiersForLogs as Optional)")
Logger.debug("\(self.logTag) .callData setter: \(oldValue?.call.identifiersForLogs as Optional) -> \(callData?.call.identifiersForLogs as Optional)")
for observer in observers {
observer.value?.didUpdateCall(call: call)
observer.value?.didUpdateCall(call: callData?.call)
}
}
}
// Used to coordinate promises across delegate methods
private var fulfillCallConnectedPromise: (() -> Void)?
private var rejectCallConnectedPromise: ((Error) -> Void)?
// Used by waitForPeerConnectionClient to make sure any received
// ICE messages wait until the peer connection client is set up.
private var fulfillPeerConnectionClientPromise: (() -> Void)?
private var rejectPeerConnectionClientPromise: ((Error) -> Void)?
private var peerConnectionClientPromise: Promise<Void>?
// Used by waituntilReadyToSendIceUpdates to make sure CallOffer was
// sent before sending any ICE updates.
private var fulfillReadyToSendIceUpdatesPromise: (() -> Void)?
private var rejectReadyToSendIceUpdatesPromise: ((Error) -> Void)?
private var readyToSendIceUpdatesPromise: Promise<Void>?
weak var localVideoTrack: RTCVideoTrack? {
didSet {
var call: SignalCall? {
get {
SwiftAssertIsOnMainThread(#function)
Logger.info("\(self.logTag) \(#function)")
fireDidUpdateVideoTracks()
return callData?.call
}
}
weak var remoteVideoTrack: RTCVideoTrack? {
didSet {
var peerConnectionClient: PeerConnectionClient? {
get {
SwiftAssertIsOnMainThread(#function)
Logger.info("\(self.logTag) \(#function)")
return callData?.peerConnectionClient
}
}
var localVideoTrack: RTCVideoTrack? {
get {
SwiftAssertIsOnMainThread(#function)
fireDidUpdateVideoTracks()
return callData?.localVideoTrack
}
}
var isRemoteVideoEnabled = false {
didSet {
var remoteVideoTrack: RTCVideoTrack? {
get {
SwiftAssertIsOnMainThread(#function)
Logger.info("\(self.logTag) \(#function): \(isRemoteVideoEnabled)")
return callData?.remoteVideoTrack
}
}
var isRemoteVideoEnabled: Bool {
get {
SwiftAssertIsOnMainThread(#function)
fireDidUpdateVideoTracks()
guard let callData = callData else {
return false
}
return callData.isRemoteVideoEnabled
}
}
@ -270,7 +358,8 @@ protocol CallServiceObserver: class {
return Promise(error: CallError.assertionError(description: errorDescription))
}
self.call = call
let callData = SignalCallData(call: call)
self.callData = callData
let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeOutgoingIncomplete, in: call.thread)
callRecord.save()
@ -283,7 +372,7 @@ protocol CallServiceObserver: class {
throw CallError.obsoleteCall(description: "obsolete call in \(#function)")
}
guard self.peerConnectionClient == nil else {
guard callData.peerConnectionClient == nil else {
let errorDescription = "\(self.logTag) peerconnection was unexpectedly already set."
Logger.error(errorDescription)
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionAlreadySet(), file: #file, function: #function, line: #line)
@ -294,8 +383,8 @@ protocol CallServiceObserver: class {
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .outgoing, useTurnOnly: useTurnOnly)
Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function) for call: \(call.identifiersForLogs)")
self.peerConnectionClient = peerConnectionClient
self.fulfillPeerConnectionClientPromise?()
callData.peerConnectionClient = peerConnectionClient
callData.fulfillPeerConnectionClientPromise()
return peerConnectionClient.createOffer()
}.then { (sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> in
@ -321,10 +410,6 @@ protocol CallServiceObserver: class {
// clients that don't support receiving ICE updates before receiving the call offer.
self.readyToSendIceUpdates(call: call)
let (callConnectedPromise, fulfill, reject) = Promise<Void>.pending()
self.fulfillCallConnectedPromise = fulfill
self.rejectCallConnectedPromise = reject
// Don't let the outgoing call ring forever. We don't support inbound ringing forever anyway.
let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
// This code will always be called, whether or not the call has timed out.
@ -333,7 +418,7 @@ protocol CallServiceObserver: class {
throw CallError.timeout(description: "timed out waiting to receive call answer")
}
return race(timeout, callConnectedPromise)
return race(timeout, callData.callConnectedPromise)
}.then {
Logger.info(self.call == call
? "\(self.logTag) outgoing call connected: \(call.identifiersForLogs)."
@ -360,22 +445,17 @@ protocol CallServiceObserver: class {
func readyToSendIceUpdates(call: SignalCall) {
SwiftAssertIsOnMainThread(#function)
guard self.call == call else {
guard let callData = self.callData else {
self.handleFailedCall(failedCall: call, error: .obsoleteCall(description:"obsolete call in \(#function)"))
return
}
if self.fulfillReadyToSendIceUpdatesPromise == nil {
createReadyToSendIceUpdatesPromise()
}
guard let fulfillReadyToSendIceUpdatesPromise = self.fulfillReadyToSendIceUpdatesPromise else {
OWSProdError(OWSAnalyticsEvents.callServiceMissingFulfillReadyToSendIceUpdatesPromise(), file: #file, function: #function, line: #line)
self.handleFailedCall(failedCall: call, error: CallError.assertionError(description: "failed to create fulfillReadyToSendIceUpdatesPromise"))
guard callData.call == call else {
self.handleFailedCall(failedCall: call, error: .obsoleteCall(description:"obsolete call in \(#function)"))
return
}
fulfillReadyToSendIceUpdatesPromise()
callData.fulfillReadyToSendIceUpdatesPromise()
}
/**
@ -559,7 +639,8 @@ protocol CallServiceObserver: class {
Logger.info("\(self.logTag) starting new call: \(newCall.identifiersForLogs)")
self.call = newCall
let callData = SignalCallData(call: newCall)
self.callData = callData
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: "\(#function)", completionBlock: { [weak self] status in
SwiftAssertIsOnMainThread(#function)
@ -598,8 +679,8 @@ protocol CallServiceObserver: class {
Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function) for: \(newCall.identifiersForLogs)")
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .incoming, useTurnOnly: useTurnOnly)
self.peerConnectionClient = peerConnectionClient
self.fulfillPeerConnectionClientPromise?()
callData.peerConnectionClient = peerConnectionClient
callData.fulfillPeerConnectionClientPromise()
let offerSessionDescription = RTCSessionDescription(type: .offer, sdp: callerSessionDescription)
let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
@ -627,8 +708,6 @@ protocol CallServiceObserver: class {
// a more intuitive ordering.
self.readyToSendIceUpdates(call: newCall)
let (promise, fulfill, reject) = Promise<Void>.pending()
let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
// rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingIncoming(), file: #file, function: #function, line: #line)
@ -636,10 +715,7 @@ protocol CallServiceObserver: class {
}
// This will be fulfilled (potentially) by the RTCDataChannel delegate method
self.fulfillCallConnectedPromise = fulfill
self.rejectCallConnectedPromise = reject
return race(promise, timeout)
return race(callData.callConnectedPromise, timeout)
}.then {
Logger.info(self.call == newCall
? "\(self.logTag) incoming call connected: \(newCall.identifiersForLogs)."
@ -660,6 +736,7 @@ protocol CallServiceObserver: class {
}.always {
Logger.debug("\(self.logTag) ending background task awaiting inbound call connection")
assert(backgroundTask != nil)
backgroundTask = nil
}
incomingCallPromise.retainUntilComplete()
@ -669,7 +746,15 @@ protocol CallServiceObserver: class {
* Remote client (could be caller or callee) sent us a connectivity update
*/
public func handleRemoteAddedIceCandidate(thread: TSContactThread, callId: UInt64, sdp: String, lineIndex: Int32, mid: String) {
waitForPeerConnectionClient().then { () -> Void in
Logger.verbose("\(logTag) \(#function) callId: \(callId)")
guard let callData = self.callData else {
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
self.handleFailedCurrentCall(error: .obsoleteCall(description: "ignoring remote ice update, since there is no current call."))
return
}
callData.peerConnectionClientPromise.then { () -> Void in
SwiftAssertIsOnMainThread(#function)
guard let call = self.call else {
@ -692,10 +777,11 @@ protocol CallServiceObserver: class {
return
}
Logger.verbose("\(self.logTag) \(#function) addRemoteIceCandidate")
peerConnectionClient.addRemoteIceCandidate(RTCIceCandidate(sdp: sdp, sdpMLineIndex: lineIndex, sdpMid: mid))
}.catch { error in
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleRemoteAddedIceCandidate(), file: #file, function: #function, line: #line)
Logger.error("\(self.logTag) in \(#function) waitForPeerConnectionClient failed with error: \(error)")
Logger.error("\(self.logTag) in \(#function) peerConnectionClientPromise failed with error: \(error)")
}.retainUntilComplete()
}
@ -706,15 +792,16 @@ protocol CallServiceObserver: class {
private func handleLocalAddedIceCandidate(_ iceCandidate: RTCIceCandidate) {
SwiftAssertIsOnMainThread(#function)
guard let call = self.call else {
guard let callData = self.callData else {
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
self.handleFailedCurrentCall(error: CallError.assertionError(description: "ignoring local ice candidate, since there is no current call."))
return
}
let call = callData.call
// Wait until we've sent the CallOffer before sending any ice updates for the call to ensure
// intuitive message ordering for other clients.
waitUntilReadyToSendIceUpdates().then { () -> Void in
callData.readyToSendIceUpdatesPromise.then { () -> Void in
guard call == self.call else {
self.handleFailedCurrentCall(error: .obsoleteCall(description: "current call changed since we became ready to send ice updates"))
return
@ -730,7 +817,7 @@ protocol CallServiceObserver: class {
let iceUpdateMessage = OWSCallIceUpdateMessage(callId: call.signalingId, sdp: iceCandidate.sdp, sdpMLineIndex: iceCandidate.sdpMLineIndex, sdpMid: iceCandidate.sdpMid)
Logger.info("\(self.logTag) in \(#function) sending ICE Candidate.")
Logger.info("\(self.logTag) in \(#function) sending ICE Candidate \(call.identifiersForLogs).")
let callMessage = OWSOutgoingCallMessage(thread: call.thread, iceUpdateMessage: iceUpdateMessage)
let sendPromise = self.messageSender.sendPromise(message: callMessage)
sendPromise.retainUntilComplete()
@ -876,13 +963,13 @@ protocol CallServiceObserver: class {
Logger.debug("\(self.logTag) in \(#function)")
guard let currentCall = self.call else {
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(self.logTag) ignoring \(#function) since there is no current call"))
guard let currentCallData = self.callData else {
OWSProdError(OWSAnalyticsEvents.callServiceCallDataMissing(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(self.logTag) callData unexpectedly nil in \(#function)"))
return
}
guard call == currentCall else {
guard call == currentCallData.call else {
// This could conceivably happen if the other party of an old call was slow to send us their answer
// and we've subsequently engaged in another call. Don't kill the current call, but just ignore it.
Logger.warn("\(self.logTag) ignoring \(#function) for call other than current call")
@ -904,14 +991,14 @@ protocol CallServiceObserver: class {
let message = DataChannelMessage.forConnected(callId: call.signalingId)
peerConnectionClient.sendDataChannelMessage(data: message.asData(), description: "connected", isCritical: true)
handleConnectedCall(call)
handleConnectedCall(currentCallData)
}
/**
* For outgoing call, when the callee has chosen to accept the call.
* For incoming call, when the local user has chosen to accept the call.
*/
func handleConnectedCall(_ call: SignalCall) {
private func handleConnectedCall(_ callData: SignalCallData) {
Logger.info("\(self.logTag) in \(#function)")
SwiftAssertIsOnMainThread(#function)
@ -921,16 +1008,15 @@ protocol CallServiceObserver: class {
return
}
Logger.info("\(self.logTag) handleConnectedCall: \(call.identifiersForLogs).")
Logger.info("\(self.logTag) handleConnectedCall: \(callData.call.identifiersForLogs).")
assert(self.fulfillCallConnectedPromise != nil)
// cancel connection timeout
self.fulfillCallConnectedPromise?()
callData.fulfillCallConnectedPromise()
call.state = .connected
callData.call.state = .connected
// We don't risk transmitting any media until the remote client has admitted to being connected.
ensureAudioState(call: call, peerConnectionClient: peerConnectionClient)
ensureAudioState(call: callData.call, peerConnectionClient: peerConnectionClient)
peerConnectionClient.setLocalVideoEnabled(enabled: shouldHaveLocalVideoTrack())
}
@ -1195,13 +1281,14 @@ protocol CallServiceObserver: class {
private func handleDataChannelMessage(_ message: OWSWebRTCProtosData) {
SwiftAssertIsOnMainThread(#function)
guard let call = self.call else {
guard let callData = self.callData else {
// This should never happen; return to a known good state.
owsFail("\(self.logTag) received data message, but there is no current call. Ignoring.")
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(self.logTag) received data message, but there is no current call. Ignoring."))
return
}
let call = callData.call
if message.hasConnected() {
Logger.debug("\(self.logTag) remote participant sent Connected via data channel: \(call.identifiersForLogs).")
@ -1217,7 +1304,7 @@ protocol CallServiceObserver: class {
}
self.callUIAdapter.recipientAcceptedCall(call)
handleConnectedCall(call)
handleConnectedCall(callData)
} else if message.hasHangup() {
Logger.debug("\(self.logTag) remote participant sent Hangup via data channel: \(call.identifiersForLogs).")
@ -1236,7 +1323,8 @@ protocol CallServiceObserver: class {
} else if message.hasVideoStreamingStatus() {
Logger.debug("\(self.logTag) remote participant sent VideoStreamingStatus via data channel: \(call.identifiersForLogs).")
self.isRemoteVideoEnabled = message.videoStreamingStatus.enabled()
callData.isRemoteVideoEnabled = message.videoStreamingStatus.enabled()
self.fireDidUpdateVideoTracks()
} else {
Logger.info("\(self.logTag) received unknown or empty DataChannelMessage: \(call.identifiersForLogs).")
}
@ -1321,105 +1409,32 @@ protocol CallServiceObserver: class {
Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient")
return
}
self.localVideoTrack = videoTrack
}
internal func peerConnectionClient(_ peerConnectionClient: PeerConnectionClient, didUpdateRemote videoTrack: RTCVideoTrack?) {
SwiftAssertIsOnMainThread(#function)
guard peerConnectionClient == self.peerConnectionClient else {
guard let callData = callData else {
Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient")
return
}
self.remoteVideoTrack = videoTrack
}
// MARK: Helpers
private func waitUntilReadyToSendIceUpdates() -> Promise<Void> {
SwiftAssertIsOnMainThread(#function)
if self.readyToSendIceUpdatesPromise == nil {
createReadyToSendIceUpdatesPromise()
}
guard let readyToSendIceUpdatesPromise = self.readyToSendIceUpdatesPromise else {
OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreateReadyToSendIceUpdatesPromise(), file: #file, function: #function, line: #line)
return Promise(error: CallError.assertionError(description: "failed to create readyToSendIceUpdatesPromise"))
}
return readyToSendIceUpdatesPromise
callData.localVideoTrack = videoTrack
fireDidUpdateVideoTracks()
}
private func createReadyToSendIceUpdatesPromise() {
internal func peerConnectionClient(_ peerConnectionClient: PeerConnectionClient, didUpdateRemote videoTrack: RTCVideoTrack?) {
SwiftAssertIsOnMainThread(#function)
guard self.readyToSendIceUpdatesPromise == nil else {
owsFail("expected readyToSendIceUpdatesPromise to be nil")
return
}
guard self.fulfillReadyToSendIceUpdatesPromise == nil else {
owsFail("expected fulfillReadyToSendIceUpdatesPromise to be nil")
guard peerConnectionClient == self.peerConnectionClient else {
Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient")
return
}
guard self.rejectReadyToSendIceUpdatesPromise == nil else {
owsFail("expected rejectReadyToSendIceUpdatesPromise to be nil")
guard let callData = callData else {
Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient")
return
}
let (promise, fulfill, reject) = Promise<Void>.pending()
self.fulfillReadyToSendIceUpdatesPromise = fulfill
self.rejectReadyToSendIceUpdatesPromise = reject
self.readyToSendIceUpdatesPromise = promise
}
private func waitForPeerConnectionClient() -> Promise<Void> {
SwiftAssertIsOnMainThread(#function)
guard self.peerConnectionClient == nil else {
// peerConnectionClient already set
return Promise(value: ())
}
if self.peerConnectionClientPromise == nil {
createPeerConnectionClientPromise()
}
guard let peerConnectionClientPromise = self.peerConnectionClientPromise else {
OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreatePeerConnectionClientPromise(), file: #file, function: #function, line: #line)
return Promise(error: CallError.assertionError(description: "failed to create peerConnectionClientPromise"))
}
return peerConnectionClientPromise
callData.remoteVideoTrack = videoTrack
fireDidUpdateVideoTracks()
}
private func createPeerConnectionClientPromise() {
SwiftAssertIsOnMainThread(#function)
guard self.peerConnectionClientPromise == nil else {
owsFail("expected peerConnectionClientPromise to be nil")
return
}
guard self.fulfillPeerConnectionClientPromise == nil else {
owsFail("expected fulfillPeerConnectionClientPromise to be nil")
return
}
guard self.rejectPeerConnectionClientPromise == nil else {
owsFail("expected rejectPeerConnectionClientPromise to be nil")
return
}
let (promise, fulfill, reject) = Promise<Void>.pending()
self.fulfillPeerConnectionClientPromise = fulfill
self.rejectPeerConnectionClientPromise = reject
self.peerConnectionClientPromise = promise
}
// MARK: -
/**
* RTCIceServers are used when attempting to establish an optimal connection to the other party. SignalService supplies
@ -1511,44 +1526,14 @@ protocol CallServiceObserver: class {
Logger.debug("\(self.logTag) in \(#function)")
self.localVideoTrack = nil
self.remoteVideoTrack = nil
self.isRemoteVideoEnabled = false
let currentCallData = self.callData
self.callData = nil
self.peerConnectionClient?.terminate()
Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function)")
self.peerConnectionClient = nil
currentCallData?.terminate()
self.call?.removeAllObservers()
self.callUIAdapter.didTerminateCall(self.call)
self.call = nil
self.fulfillCallConnectedPromise = nil
// In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak.
// There is no harm in rejecting a previously fulfilled promise.
if let rejectCallConnectedPromise = self.rejectCallConnectedPromise {
rejectCallConnectedPromise(CallError.obsoleteCall(description: "Terminating call"))
}
self.rejectCallConnectedPromise = nil
// In case we're still waiting on the peer connection setup somewhere, we need to reject it to avoid a memory leak.
// There is no harm in rejecting a previously fulfilled promise.
if let rejectPeerConnectionClientPromise = self.rejectPeerConnectionClientPromise {
rejectPeerConnectionClientPromise(CallError.obsoleteCall(description: "Terminating call"))
}
self.rejectPeerConnectionClientPromise = nil
self.fulfillPeerConnectionClientPromise = nil
self.peerConnectionClientPromise = nil
// In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak.
// There is no harm in rejecting a previously fulfilled promise.
if let rejectReadyToSendIceUpdatesPromise = self.rejectReadyToSendIceUpdatesPromise {
rejectReadyToSendIceUpdatesPromise(CallError.obsoleteCall(description: "Terminating call"))
}
self.fulfillReadyToSendIceUpdatesPromise = nil
self.rejectReadyToSendIceUpdatesPromise = nil
self.readyToSendIceUpdatesPromise = nil
fireDidUpdateVideoTracks()
}
// MARK: - CallObserver

@ -166,6 +166,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
}
}
deinit {
Logger.debug("[PeerConnectionClient] deinit")
}
// MARK: - Media Streams
private func createSignalingDataChannel() {

@ -76,6 +76,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)callServicePeerConnectionMissing;
+ (NSString *)callServiceCallDataMissing;
+ (NSString *)contactsErrorContactsIntersectionFailed;
+ (NSString *)errorAttachmentRequestFailed;

@ -157,6 +157,11 @@ NS_ASSUME_NONNULL_BEGIN
return @"call_service_peer_connection_missing";
}
+ (NSString *)callServiceCallDataMissing
{
return @"call_service_call_data_missing";
}
+ (NSString *)contactsErrorContactsIntersectionFailed
{
return @"contacts_error_contacts_intersection_failed";

Loading…
Cancel
Save