@ -177,6 +177,12 @@ protocol CallServiceObserver: class {
private var rejectPeerConnectionClientPromise : ( ( Error ) -> Void ) ?
private var peerConnectionClientPromise : Promise < Void > ?
// U s e d b y w a i t u n t i l R e a d y T o S e n d I c e U p d a t e s t o m a k e s u r e C a l l O f f e r w a s
// s e n t b e f o r e s e n d i n g a n y I C E u p d a t e s .
private var fulfillReadyToSendIceUpdatesPromise : ( ( ) -> Void ) ?
private var rejectReadyToSendIceUpdatesPromise : ( ( Error ) -> Void ) ?
private var readyToSendIceUpdatesPromise : Promise < Void > ?
weak var localVideoTrack : RTCVideoTrack ? {
didSet {
AssertIsOnMainThread ( )
@ -318,6 +324,10 @@ protocol CallServiceObserver: class {
throw CallError . obsoleteCall ( description : " obsolete call in \( #function ) " )
}
// F o r o u t g o i n g c a l l s , w a i t u n t i l c a l l o f f e r i s s e n t b e f o r e w e s e n d a n y I C E u p d a t e s , t o e n s u r e m e s s a g e o r d e r i n g f o r
// c l i e n t s t h a t d o n ' t s u p p o r t r e c e i v i n g I C E u p d a t e s b e f o r e r e c e i v i n g t h e c a l l o f f e r .
self . readyToSendIceUpdates ( call : call )
let ( callConnectedPromise , fulfill , _ ) = Promise < Void > . pending ( )
self . fulfillCallConnectedPromise = fulfill
@ -346,6 +356,26 @@ protocol CallServiceObserver: class {
return promise
}
func readyToSendIceUpdates ( call : SignalCall ) {
AssertIsOnMainThread ( )
guard self . call = = call else {
self . handleFailedCall ( failedCall : call , error : . obsoleteCall ( description : " obsolete call in \( #function ) " ) )
return
}
if self . fulfillReadyToSendIceUpdatesPromise = = nil {
createReadyToSendIceUpdatesPromise ( )
}
guard let fulfillReadyToSendIceUpdatesPromise = self . fulfillReadyToSendIceUpdatesPromise else {
self . handleFailedCall ( failedCall : call , error : . assertionError ( description : " failed to create fulfillReadyToSendIceUpdatesPromise " ) )
return
}
fulfillReadyToSendIceUpdatesPromise ( )
}
/* *
* Called by the call initiator after receiving a CallAnswer from the callee .
*/
@ -586,6 +616,10 @@ protocol CallServiceObserver: class {
}
Logger . debug ( " \( self . TAG ) successfully sent callAnswerMessage for: \( newCall . identifiersForLogs ) " )
// T h e r e ' s n o t h i n g t e c h n i c a l l y f o r b i d d i n g r e c e i v i n g I C E u p d a t e s b e f o r e r e c e i v i n g t h e C a l l A n s w e r , b u t t h i s
// a m o r e i n t u i t i v e o r d e r i n g .
self . readyToSendIceUpdates ( call : newCall )
let ( promise , fulfill , _ ) = Promise < Void > . pending ( )
let timeout : Promise < Void > = after ( interval : TimeInterval ( connectingTimeoutSeconds ) ) . then { ( ) -> Void in
@ -648,7 +682,7 @@ protocol CallServiceObserver: class {
peerConnectionClient . addRemoteIceCandidate ( RTCIceCandidate ( sdp : sdp , sdpMLineIndex : lineIndex , sdpMid : mid ) )
} . catch { error in
Logger. error ( " \( self . TAG ) in \( #function ) failed with error: \( error ) " )
owsFail ( " \( self . TAG ) in \( #function ) waitForPeerConnectionClient failed with error: \( error ) " )
} . retainUntilComplete ( )
}
@ -660,34 +694,43 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread ( )
guard let call = self . call else {
// T h i s w i l l o n l y b e c a l l e d f o r t h e c u r r e n t p e e r C o n n e c t i o n C l i e n t , s o
// f a i l t h e c u r r e n t c a l l .
handleFailedCurrentCall ( error : . assertionError ( description : " ignoring local ice candidate, since there is no current call. " ) )
self . handleFailedCurrentCall ( error : . assertionError ( description : " ignoring local ice candidate, since there is no current call. " ) )
return
}
guard call . state != . idle else {
// T h i s w i l l o n l y b e c a l l e d f o r t h e c u r r e n t p e e r C o n n e c t i o n C l i e n t , s o
// f a i l t h e c u r r e n t c a l l .
handleFailedCurrentCall ( error : . assertionError ( description : " ignoring local ice candidate, since call is now idle. " ) )
return
}
// W a i t u n t i l w e ' v e s e n t t h e C a l l O f f e r b e f o r e s e n d i n g a n y i c e u p d a t e s f o r t h e c a l l t o e n s u r e
// i n t u i t i v e m e s s a g e o r d e r i n g f o r o t h e r c l i e n t s .
waitUntilReadyToSendIceUpdates ( ) . 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
}
let iceUpdateMessage = OWSCallIceUpdateMessage ( callId : call . signalingId , sdp : iceCandidate . sdp , sdpMLineIndex : iceCandidate . sdpMLineIndex , sdpMid : iceCandidate . sdpMid )
guard call . state != . idle else {
// T h i s w i l l o n l y b e c a l l e d f o r t h e c u r r e n t p e e r C o n n e c t i o n C l i e n t , s o
// f a i l t h e c u r r e n t c a l l .
self . handleFailedCurrentCall ( error : . assertionError ( description : " ignoring local ice candidate, since call is now idle. " ) )
return
}
if self . sendIceUpdatesImmediately {
Logger . info ( " \( TAG ) in \( #function ) . Sending immediately. " )
let callMessage = OWSOutgoingCallMessage ( thread : call . thread , iceUpdateMessage : iceUpdateMessage )
let sendPromise = self . messageSender . sendCallMessage ( callMessage )
sendPromise . retainUntilComplete ( )
} else {
// F o r o u t g o i n g m e s s a g e s , w e w a i t t o s e n d i c e u p d a t e s u n t i l w e ' r e s u r e c l i e n t r e c e i v e d o u r c a l l m e s s a g e .
// e . g . i f t h e c l i e n t h a s b l o c k e d o u r m e s s a g e d u e t o a n i d e n t i t y c h a n g e , w e ' d o t h e r w i s e
// b o m b a r d t h e m w i t h a b u n c h * m o r e * u n d e c i p h e r a b l e m e s s a g e s .
Logger . info ( " \( TAG ) in \( #function ) . Enqueing for later. " )
self . pendingIceUpdateMessages . append ( iceUpdateMessage )
return
}
let iceUpdateMessage = OWSCallIceUpdateMessage ( callId : call . signalingId , sdp : iceCandidate . sdp , sdpMLineIndex : iceCandidate . sdpMLineIndex , sdpMid : iceCandidate . sdpMid )
if self . sendIceUpdatesImmediately {
Logger . info ( " \( self . TAG ) in \( #function ) . Sending immediately. " )
let callMessage = OWSOutgoingCallMessage ( thread : call . thread , iceUpdateMessage : iceUpdateMessage )
let sendPromise = self . messageSender . sendCallMessage ( callMessage )
sendPromise . retainUntilComplete ( )
} else {
// F o r o u t g o i n g m e s s a g e s , w e w a i t t o s e n d i c e u p d a t e s u n t i l w e ' r e s u r e c l i e n t r e c e i v e d o u r c a l l m e s s a g e .
// e . g . i f t h e c l i e n t h a s b l o c k e d o u r m e s s a g e d u e t o a n i d e n t i t y c h a n g e , w e ' d o t h e r w i s e
// b o m b a r d t h e m w i t h a b u n c h * m o r e * u n d e c i p h e r a b l e m e s s a g e s .
Logger . info ( " \( self . TAG ) in \( #function ) . Enqueing for later. " )
self . pendingIceUpdateMessages . append ( iceUpdateMessage )
return
}
} . catch { error in
owsFail ( " \( self . TAG ) in \( #function ) waitUntilReadyToSendIceUpdates failed with error: \( error ) " )
} . retainUntilComplete ( )
}
/* *
@ -1175,6 +1218,44 @@ protocol CallServiceObserver: class {
// MARK: H e l p e r s
private func waitUntilReadyToSendIceUpdates ( ) -> Promise < Void > {
AssertIsOnMainThread ( )
if self . readyToSendIceUpdatesPromise = = nil {
createReadyToSendIceUpdatesPromise ( )
}
guard let readyToSendIceUpdatesPromise = self . readyToSendIceUpdatesPromise else {
return Promise ( error : CallError . assertionError ( description : " failed to create readyToSendIceUpdatesPromise " ) )
}
return readyToSendIceUpdatesPromise
}
private func createReadyToSendIceUpdatesPromise ( ) {
AssertIsOnMainThread ( )
guard self . readyToSendIceUpdatesPromise = = nil else {
Logger . error ( " expected readyToSendIceUpdatesPromise to be nil " )
return
}
guard self . fulfillReadyToSendIceUpdatesPromise = = nil else {
Logger . error ( " expected fulfillReadyToSendIceUpdatesPromise to be nil " )
return
}
guard self . rejectReadyToSendIceUpdatesPromise = = nil else {
Logger . error ( " expected rejectReadyToSendIceUpdatesPromise to be nil " )
return
}
let ( promise , fulfill , reject ) = Promise < Void > . pending ( )
self . fulfillReadyToSendIceUpdatesPromise = fulfill
self . rejectReadyToSendIceUpdatesPromise = reject
self . readyToSendIceUpdatesPromise = promise
}
private func waitForPeerConnectionClient ( ) -> Promise < Void > {
AssertIsOnMainThread ( )
@ -1324,6 +1405,15 @@ protocol CallServiceObserver: class {
self . rejectPeerConnectionClientPromise = nil
self . fulfillPeerConnectionClientPromise = nil
self . peerConnectionClientPromise = nil
// I n c a s e w e ' r e s t i l l w a i t i n g o n t h i s p r o m i s e s o m e w h e r e , w e n e e d t o r e j e c t i t t o a v o i d a m e m o r y l e a k .
// T h e r e i s n o h a r m i n r e j e c t i n g a p r e v i o u s l y f u l f i l l e d p r o m i s e .
if let rejectReadyToSendIceUpdatesPromise = self . rejectReadyToSendIceUpdatesPromise {
rejectReadyToSendIceUpdatesPromise ( CallError . obsoleteCall ( description : " Terminating call " ) )
}
self . fulfillReadyToSendIceUpdatesPromise = nil
self . rejectReadyToSendIceUpdatesPromise = nil
self . readyToSendIceUpdatesPromise = nil
}
// MARK: - C a l l O b s e r v e r