@ -75,7 +75,7 @@ enum CallError: Error {
// F I X M E T O D O d o w e n e e d t o t i m e o u t ?
fileprivate let timeoutSeconds = 60
@objc class CallService : NSObject , RTCDataChannelDelegate, RTCPeerConnection Delegate {
@objc class CallService : NSObject , PeerConnectionClientDelegate, RTCDataChannel Delegate {
// MARK: - P r o p e r t i e s
@ -167,7 +167,7 @@ fileprivate let timeoutSeconds = 60
return getIceServers ( ) . then ( on : CallService . signalingQueue ) { iceServers -> Promise < HardenedRTCSessionDescription > in
Logger . debug ( " \( self . TAG ) got ice servers: \( iceServers ) " )
let peerConnectionClient = PeerConnectionClient ( iceServers : iceServers , peerConnectionD elegate: self )
let peerConnectionClient = PeerConnectionClient ( iceServers : iceServers , d elegate: self )
self . peerConnectionClient = peerConnectionClient
// W h e n c a l l i n g , i t ' s o u r r e s p o n s i b i l i t y t o c r e a t e t h e D a t a C h a n n e l . R e c e i v e r s w i l l n o t h a v e t o d o t h i s e x p l i c i t l y .
@ -315,7 +315,7 @@ fileprivate let timeoutSeconds = 60
} . then ( on : CallService . signalingQueue ) { ( iceServers : [ RTCIceServer ] ) -> Promise < HardenedRTCSessionDescription > in
// F I X M E f o r f i r s t t i m e c a l l r e c i p i e n t s I t h i n k w e ' l l s e e m i c / c a m e r a p e r m i s s i o n r e q u e s t s h e r e ,
// e v e n t h o u g h , f r o m t h e u s e r s p e r s p e c t i v e , n o i n c o m i n g c a l l i s y e t v i s i b l e .
self . peerConnectionClient = PeerConnectionClient ( iceServers : iceServers , peerConnectionD elegate: self )
self . peerConnectionClient = PeerConnectionClient ( iceServers : iceServers , d elegate: self )
let offerSessionDescription = RTCSessionDescription ( type : . offer , sdp : callerSessionDescription )
let constraints = RTCMediaConstraints ( mandatoryConstraints : nil , optionalConstraints : nil )
@ -323,7 +323,6 @@ fileprivate let timeoutSeconds = 60
// F i n d a s e s s i o n D e s c r i p t i o n c o m p a t i b l e w i t h m y c o n s t r a i n t s a n d t h e r e m o t e s e s s i o n D e s c r i p t i o n
return self . peerConnectionClient ! . negotiateSessionDescription ( remoteDescription : offerSessionDescription , constraints : constraints )
} . then ( on : CallService . signalingQueue ) { ( negotiatedSessionDescription : HardenedRTCSessionDescription ) in
// T O D O ? W e b R t c C a l l S e r v i c e . t h i s . l o c k M a n a g e r . u p d a t e P h o n e S t a t e ( L o c k M a n a g e r . P h o n e S t a t e . P R O C E S S I N G ) ;
Logger . debug ( " \( self . TAG ) set the remote description " )
let answerMessage = OWSCallAnswerMessage ( callId : newCall . signalingId , sessionDescription : negotiatedSessionDescription . sdp )
@ -784,6 +783,37 @@ fileprivate let timeoutSeconds = 60
}
}
// MARK: - P e e r C o n n e c t i o n C l i e n t D e l e g a t e
/* *
* The connection has been established . The clients can now communicate .
*/
internal func peerConnectionClientIceConnected ( _ peerconnectionClient : PeerConnectionClient ) {
CallService . signalingQueue . async {
self . handleIceConnected ( )
}
}
/* *
* The connection failed to establish . The clients will not be able to communicate .
*/
internal func peerConnectionClientIceFailed ( _ peerconnectionClient : PeerConnectionClient ) {
CallService . signalingQueue . async {
self . handleFailedCall ( error : CallError . disconnected )
}
}
/* *
* During the Signaling process each client generates IceCandidates locally , which contain information about how to
* reach the local client via the internet . The delegate must shuttle these IceCandates to the other ( remote ) client
* out of band , as part of establishing a connection over WebRTC .
*/
internal func peerConnectionClient ( _ peerconnectionClient : PeerConnectionClient , addedLocalIceCandidate iceCandidate : RTCIceCandidate ) {
CallService . signalingQueue . async {
self . handleLocalAddedIceCandidate ( iceCandidate )
}
}
// MARK: H e l p e r s
/* *
@ -799,7 +829,8 @@ fileprivate let timeoutSeconds = 60
}
/* *
*
* RTCIceServers are used when attempting to establish an optimal connection to the other party . SignalService supplies
* a list of servers , plus we have fallback servers hardcoded in the app .
*/
private func getIceServers ( ) -> Promise < [ RTCIceServer ] > {
return firstly {
@ -836,38 +867,15 @@ fileprivate let timeoutSeconds = 60
terminateCall ( )
}
/* *
* Clean up any existing call state and get ready to receive a new call .
*/
private func terminateCall ( ) {
assertOnSignalingQueue ( )
// l o c k M a n a g e r . u p d a t e P h o n e S t a t e ( L o c k M a n a g e r . P h o n e S t a t e . P R O C E S S I N G ) ;
// N o t i f i c a t i o n B a r M a n a g e r . s e t C a l l E n d e d ( t h i s ) ;
//
// i n c o m i n g R i n g e r . s t o p ( ) ;
// o u t g o i n g R i n g e r . s t o p ( ) ;
// o u t g o i n g R i n g e r . p l a y D i s c o n n e c t e d ( ) ;
//
// i f ( p e e r C o n n e c t i o n ! = n u l l ) {
// p e e r C o n n e c t i o n . d i s p o s e ( ) ;
// p e e r C o n n e c t i o n = n u l l ;
// }
//
// i f ( e g l B a s e ! = n u l l & & l o c a l R e n d e r e r ! = n u l l & & r e m o t e R e n d e r e r ! = n u l l ) {
// l o c a l R e n d e r e r . r e l e a s e ( ) ;
// r e m o t e R e n d e r e r . r e l e a s e ( ) ;
// e g l B a s e . r e l e a s e ( ) ;
// }
//
// s h u t d o w n A u d i o ( ) ;
//
// t h i s . c a l l S t a t e = C a l l S t a t e . S T A T E _ I D L E ;
// t h i s . r e c i p i e n t = n u l l ;
// t h i s . c a l l I d = n u l l ;
// t h i s . a u d i o E n a b l e d = f a l s e ;
// t h i s . v i d e o E n a b l e d = f a l s e ;
// t h i s . p e n d i n g I c e U p d a t e s = n u l l ;
// l o c k M a n a g e r . u p d a t e P h o n e S t a t e ( L o c k M a n a g e r . P h o n e S t a t e . I D L E ) ;
Logger . debug ( " \( TAG ) in \( #function ) " )
peerConnectionClient ? . terminate ( )
peerConnectionClient = nil
call = nil
thread = nil
@ -877,11 +885,11 @@ fileprivate let timeoutSeconds = 60
}
// MARK: - R T C D a t a C h a n n e l D e l e g a t e
// T O D O m o v e ` R T C D a t a C h a n n e l D e l e g a t e ` s t u f f i n t o p e e r C o n n e c t i o n C l i e n t a n d a d d a m e t h o d t o p e e r C o n n e c t i o n C l i e n t D e l e g a t e ` r e c e i v e D a t a C h a n n e l M s s a g e ( _ m e s s a g e : O W S W e b R T C P r o t o s )
/* * T h e d a t a c h a n n e l s t a t e c h a n g e d . */
public func dataChannelDidChangeState ( _ dataChannel : RTCDataChannel ) {
Logger . debug ( " \( TAG ) dataChannelDidChangeState: \( dataChannel ) " )
// S i g n a l i n g Q u e u e . d i s p a t c h . a s y n c { }
}
/* * T h e d a t a c h a n n e l s u c c e s s f u l l y r e c e i v e d a d a t a b u f f e r . */
@ -903,138 +911,6 @@ fileprivate let timeoutSeconds = 60
public func dataChannel ( _ dataChannel : RTCDataChannel , didChangeBufferedAmount amount : UInt64 ) {
Logger . debug ( " \( TAG ) didChangeBufferedAmount: \( amount ) " )
}
// MARK: - R T C P e e r C o n n e c t i o n D e l e g a t e
/* * C a l l e d w h e n t h e S i g n a l i n g S t a t e c h a n g e d . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didChange stateChanged : RTCSignalingState ) {
Logger . debug ( " \( TAG ) didChange signalingState: \( stateChanged . debugDescription ) " )
}
/* * C a l l e d w h e n m e d i a i s r e c e i v e d o n a n e w s t r e a m f r o m r e m o t e p e e r . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didAdd stream : RTCMediaStream ) {
Logger . debug ( " \( TAG ) didAdd stream: \( stream ) " )
}
/* * C a l l e d w h e n a r e m o t e p e e r c l o s e s a s t r e a m . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didRemove stream : RTCMediaStream ) {
Logger . debug ( " \( TAG ) didRemove Stream: \( stream ) " )
}
/* * C a l l e d w h e n n e g o t i a t i o n i s n e e d e d , f o r e x a m p l e I C E h a s r e s t a r t e d . */
public func peerConnectionShouldNegotiate ( _ peerConnection : RTCPeerConnection ) {
Logger . debug ( " \( TAG ) shouldNegotiate " )
}
/* * C a l l e d a n y t i m e t h e I c e C o n n e c t i o n S t a t e c h a n g e s . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didChange newState : RTCIceConnectionState ) {
Logger . debug ( " \( TAG ) didChange IceConnectionState: \( newState . debugDescription ) " )
CallService . signalingQueue . async {
switch newState {
case . connected , . completed :
self . handleIceConnected ( )
case . failed :
Logger . warn ( " \( self . TAG ) RTCIceConnection failed. " )
guard self . thread != nil else {
Logger . error ( " \( self . TAG ) refusing to hangup for failed IceConnection because there is no current thread " )
return
}
self . handleFailedCall ( error : CallError . disconnected )
default :
Logger . debug ( " \( self . TAG ) ignoring change IceConnectionState: \( newState . debugDescription ) " )
}
}
}
/* * C a l l e d a n y t i m e t h e I c e G a t h e r i n g S t a t e c h a n g e s . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didChange newState : RTCIceGatheringState ) {
Logger . debug ( " \( TAG ) didChange IceGatheringState: \( newState . debugDescription ) " )
}
/* * N e w i c e c a n d i d a t e h a s b e e n f o u n d . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didGenerate candidate : RTCIceCandidate ) {
Logger . debug ( " \( TAG ) didGenerate IceCandidate: \( candidate . sdp ) " )
CallService . signalingQueue . async {
self . handleLocalAddedIceCandidate ( candidate )
}
}
/* * C a l l e d w h e n a g r o u p o f l o c a l I c e c a n d i d a t e s h a v e b e e n r e m o v e d . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didRemove candidates : [ RTCIceCandidate ] ) {
Logger . debug ( " \( TAG ) didRemove IceCandidates: \( candidates ) " )
}
/* * N e w d a t a c h a n n e l h a s b e e n o p e n e d . */
public func peerConnection ( _ peerConnection : RTCPeerConnection , didOpen dataChannel : RTCDataChannel ) {
Logger . debug ( " \( TAG ) didOpen dataChannel: \( dataChannel ) " )
CallService . signalingQueue . async {
guard let peerConnectionClient = self . peerConnectionClient else {
Logger . error ( " \( self . TAG ) surprised to find nil peerConnectionClient in \( #function ) " )
return
}
Logger . debug ( " \( self . TAG ) set dataChannel " )
peerConnectionClient . dataChannel = dataChannel
}
}
}
// M a r k : P r e t t y P r i n t O b j c e n u m s .
fileprivate extension RTCSignalingState {
var debugDescription : String {
switch self {
case . stable :
return " stable "
case . haveLocalOffer :
return " haveLocalOffer "
case . haveLocalPrAnswer :
return " haveLocalPrAnswer "
case . haveRemoteOffer :
return " haveRemoteOffer "
case . haveRemotePrAnswer :
return " haveRemotePrAnswer "
case . closed :
return " closed "
}
}
}
fileprivate extension RTCIceGatheringState {
var debugDescription : String {
switch self {
case . new :
return " new "
case . gathering :
return " gathering "
case . complete :
return " complete "
}
}
}
fileprivate extension RTCIceConnectionState {
var debugDescription : String {
switch self {
case . new :
return " new "
case . checking :
return " checking "
case . connected :
return " connected "
case . completed :
return " completed "
case . failed :
return " failed "
case . disconnected :
return " disconnected "
case . closed :
return " closed "
case . count :
return " count "
}
}
}
fileprivate extension MessageSender {