@ -32,8 +32,8 @@ protocol PeerConnectionClientDelegate: class {
func peerConnectionClientIceFailed ( _ peerconnectionClient : PeerConnectionClient )
func peerConnectionClientIceFailed ( _ peerconnectionClient : PeerConnectionClient )
/* *
/* *
* During the Signaling process each client generates IceCandidates locally , which contain information about how to
* 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
* 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 .
* out of band , as part of establishing a connection over WebRTC .
*/
*/
func peerConnectionClient ( _ peerconnectionClient : PeerConnectionClient , addedLocalIceCandidate iceCandidate : RTCIceCandidate )
func peerConnectionClient ( _ peerconnectionClient : PeerConnectionClient , addedLocalIceCandidate iceCandidate : RTCIceCandidate )
@ -57,7 +57,7 @@ protocol PeerConnectionClientDelegate: class {
/* *
/* *
* ` PeerConnectionClient ` is our interface to WebRTC .
* ` PeerConnectionClient ` is our interface to WebRTC .
*
*
* It is primarily a wrapper around ` RTCPeerConnection ` , which is responsible for sending and receiving our call data
* It is primarily a wrapper around ` RTCPeerConnection ` , which is responsible for sending and receiving our call data
* including audio , video , and some post - connected signaling ( hangup , add video )
* including audio , video , and some post - connected signaling ( hangup , add video )
*/
*/
class PeerConnectionClient : NSObject , RTCPeerConnectionDelegate , RTCDataChannelDelegate {
class PeerConnectionClient : NSObject , RTCPeerConnectionDelegate , RTCDataChannelDelegate {
@ -65,9 +65,9 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
let TAG = " [PeerConnectionClient] "
let TAG = " [PeerConnectionClient] "
enum Identifiers : String {
enum Identifiers : String {
case mediaStream = " ARDAMS " ,
case mediaStream = " ARDAMS " ,
videoTrack = " ARDAMSv0 " ,
videoTrack = " ARDAMSv0 " ,
audioTrack = " ARDAMSa0 " ,
audioTrack = " ARDAMSa0 " ,
dataChannelSignaling = " signaling "
dataChannelSignaling = " signaling "
}
}
// A s t a t e i n t h i s c l a s s s h o u l d o n l y b e a c c e s s e d o n t h i s q u e u e i n o r d e r t o
// A s t a t e i n t h i s c l a s s s h o u l d o n l y b e a c c e s s e d o n t h i s q u e u e i n o r d e r t o
@ -155,11 +155,11 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
fileprivate func createVideoSender ( ) {
fileprivate func createVideoSender ( ) {
AssertIsOnMainThread ( )
AssertIsOnMainThread ( )
Logger . debug ( " \( self . TAG ) in \( #function ) " )
Logger . debug ( " \( TAG ) in \( #function ) " )
assert ( self . videoSender = = nil , " \( #function ) should only be called once. " )
assert ( self . videoSender = = nil , " \( #function ) should only be called once. " )
guard ! Platform . isSimulator else {
guard ! Platform . isSimulator else {
Logger . warn ( " \( self . TAG ) Refusing to create local video track on simulator which has no capture device. " )
Logger . warn ( " \( TAG ) Refusing to create local video track on simulator which has no capture device. " )
return
return
}
}
@ -209,7 +209,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
fileprivate func createAudioSender ( ) {
fileprivate func createAudioSender ( ) {
AssertIsOnMainThread ( )
AssertIsOnMainThread ( )
Logger . debug ( " \( self . TAG ) in \( #function ) " )
Logger . debug ( " \( TAG ) in \( #function ) " )
assert ( self . audioSender = = nil , " \( #function ) should only be called once. " )
assert ( self . audioSender = = nil , " \( #function ) should only be called once. " )
let audioSource = factory . audioSource ( with : self . audioConstraints )
let audioSource = factory . audioSource ( with : self . audioConstraints )
@ -253,10 +253,15 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
}
}
public func createOffer ( ) -> Promise < HardenedRTCSessionDescription > {
public func createOffer ( ) -> Promise < HardenedRTCSessionDescription > {
var result : Promise < HardenedRTCSessionDescription > ? = nil
AssertIsOnMainThread ( )
PeerConnectionClient . signalingQueue . sync {
result = Promise { fulfill , reject in
return Promise { fulfill , reject in
peerConnection . offer ( for : self . defaultOfferConstraints , completionHandler : { ( sdp : RTCSessionDescription ? , error : Error ? ) in
AssertIsOnMainThread ( )
PeerConnectionClient . signalingQueue . async {
self . assertOnSignalingQueue ( )
self . peerConnection . offer ( for : self . defaultOfferConstraints , completionHandler : { ( sdp : RTCSessionDescription ? , error : Error ? ) in
PeerConnectionClient . signalingQueue . async {
PeerConnectionClient . signalingQueue . async {
guard error = = nil else {
guard error = = nil else {
reject ( error ! )
reject ( error ! )
@ -275,63 +280,55 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
} )
} )
}
}
}
}
// TODO: P r o p a g a t e e x c e p t i o n
return result !
}
}
public func setLocalSessionDescriptionInternal ( _ sessionDescription : HardenedRTCSessionDescription ) -> Promise < Void > {
public func setLocalSessionDescriptionInternal ( _ sessionDescription : HardenedRTCSessionDescription ) -> Promise < Void > {
assertOnSignalingQueue ( )
return PromiseKit . wrap { resolve in
self . assertOnSignalingQueue ( )
return PromiseKit . wrap {
Logger . verbose ( " \( self . TAG ) setting local session description: \( sessionDescription ) " )
Logger . verbose ( " \( self . TAG ) setting local session description: \( sessionDescription ) " )
peerConnection . setLocalDescription ( sessionDescription . rtcSessionDescription , completionHandler : $0 )
self . peerConnection . setLocalDescription ( sessionDescription . rtcSessionDescription , completionHandler : resolve )
}
}
}
}
public func setLocalSessionDescription ( _ sessionDescription : HardenedRTCSessionDescription ) -> Promise < Void > {
public func setLocalSessionDescription ( _ sessionDescription : HardenedRTCSessionDescription ) -> Promise < Void > {
var result : Promise < Void > ? = nil
AssertIsOnMainThread ( )
PeerConnectionClient . signalingQueue . sync {
result = setLocalSessionDescriptionInternal ( sessionDescription )
}
// TODO: P r o p a g a t e e x c e p t i o n
return result !
}
public func negotiateSessionDescription ( remoteDescription : RTCSessionDescription , constraints : RTCMediaConstraints ) -> Promise < HardenedRTCSessionDescription > {
return PromiseKit . wrap { resolve in
var result : Promise < HardenedRTCSessionDescription > ? = nil
PeerConnectionClient . signalingQueue . async {
PeerConnectionClient . signalingQueue . sync {
self . assertOnSignalingQueue ( )
result = firstly {
Logger . verbose ( " \( self . TAG ) setting local session description: \( sessionDescription ) " )
return self . setRemoteSessionDescriptionInternal ( remoteDescription )
self . peerConnection . setLocalDescription ( sessionDescription . rtcSessionDescription , completionHandler : resolve )
} . then ( on : PeerConnectionClient . signalingQueue ) {
return self . negotiateAnswerSessionDescription ( constraints : constraints )
}
}
}
}
// TODO: P r o p a g a t e e x c e p t i o n
return result !
}
}
p rivate func setRemoteSessionDescriptionInternal ( _ sessionDescription : RTCSessionDescription ) -> Promise < Void > {
p ublic func negotiateSessionDescription ( remoteDescription : RTCSessionDescription , constraints : RTCMediaConstraints ) -> Promise < HardenedRTCSessionDescription > {
assertOnSignalingQueue ( )
AssertIsOnMainThread ( )
return PromiseKit. wrap {
return setRemoteSessionDescription( remoteDescription )
Logger . verbose ( " \( self . TAG ) setting remote description: \( sessionDescription ) " )
. then ( on : PeerConnectionClient . signalingQueue ) {
peerConnection . setRemoteDescription ( sessionDescription , completionHandler : $0 )
return self . negotiateAnswerSessionDescription ( constraints : constraints )
}
}
}
}
public func setRemoteSessionDescription ( _ sessionDescription : RTCSessionDescription ) -> Promise < Void > {
public func setRemoteSessionDescription ( _ sessionDescription : RTCSessionDescription ) -> Promise < Void > {
var result : Promise < Void > ? = nil
AssertIsOnMainThread ( )
PeerConnectionClient . signalingQueue . sync {
result = setRemoteSessionDescriptionInternal ( sessionDescription )
return PromiseKit . wrap { resolve in
PeerConnectionClient . signalingQueue . async {
self . assertOnSignalingQueue ( )
Logger . verbose ( " \( self . TAG ) setting remote description: \( sessionDescription ) " )
self . peerConnection . setRemoteDescription ( sessionDescription , completionHandler : resolve )
}
}
}
// TODO: P r o p a g a t e e x c e p t i o n
return result !
}
}
private func negotiateAnswerSessionDescription ( constraints : RTCMediaConstraints ) -> Promise < HardenedRTCSessionDescription > {
private func negotiateAnswerSessionDescription ( constraints : RTCMediaConstraints ) -> Promise < HardenedRTCSessionDescription > {
assertOnSignalingQueue ( )
assertOnSignalingQueue ( )
return Promise { fulfill , reject in
return Promise { fulfill , reject in
assertOnSignalingQueue ( )
Logger . debug ( " \( self . TAG ) negotiating answer session. " )
Logger . debug ( " \( self . TAG ) negotiating answer session. " )
peerConnection . answer ( for : constraints , completionHandler : { ( sdp : RTCSessionDescription ? , error : Error ? ) in
peerConnection . answer ( for : constraints , completionHandler : { ( sdp : RTCSessionDescription ? , error : Error ? ) in
@ -370,36 +367,46 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func terminate ( ) {
public func terminate ( ) {
PeerConnectionClient . signalingQueue . async {
PeerConnectionClient . signalingQueue . async {
// S o m e n o t e s o n p r e v e n t i n g c r a s h e s w h i l e d i s p o s i n g o f p e e r C o n n e c t i o n f o r v i d e o c a l l s
self . terminateInternal ( )
// f r o m : h t t p s : / / g r o u p s . g o o g l e . c o m / f o r u m / # ! s e a r c h i n / d i s c u s s - w e b r t c / o b j c $ 2 0 c r a s h $ 2 0 d e a l l o c % 7 C s o r t : r e l e v a n c e / d i s c u s s - w e b r t c / 7 D - v k 5 y L j n 8 / r B W 2 D 6 E W 4 G Y J
// T h e s e q u e n c e t o m a k e i t w o r k a p p e a r s t o b e
//
// [ c a p t u r e r s t o p ] ; / / I h a d t o a d d t h i s a s a m e t h o d t o R T C V i d e o C a p t u r e r
// [ l o c a l R e n d e r e r s t o p ] ;
// [ r e m o t e R e n d e r e r s t o p ] ;
// [ p e e r C o n n e c t i o n c l o s e ] ;
// a u d i o T r a c k i s a s t r o n g p r o p e r t y b e c a u s e w e n e e d a c c e s s t o i t t o m u t e / u n m u t e , b u t I w a s s e e i n g i t
// b e c o m e n i l w h e n i t w a s o n l y a w e a k p r o p e r t y . S o w e r e t a i n i t a n d m a n u a l l y n i l t h e r e f e r e n c e h e r e , b e c a u s e
// w e a r e l i k e l y t o c r a s h i f w e r e t a i n a n y p e e r c o n n e c t i o n p r o p e r t i e s w h e n t h e p e e r c o n n e c t i o n i s r e l e a s e d
Logger . debug ( " \( self . TAG ) in \( #function ) " )
self . audioTrack = nil
self . localVideoTrack = nil
self . remoteVideoTrack = nil
self . dataChannel = nil
self . audioSender = nil
self . videoSender = nil
self . peerConnection . delegate = nil
self . peerConnection . close ( )
}
}
}
}
private func terminateInternal ( ) {
assertOnSignalingQueue ( )
// S o m e n o t e s o n p r e v e n t i n g c r a s h e s w h i l e d i s p o s i n g o f p e e r C o n n e c t i o n f o r v i d e o c a l l s
// f r o m : h t t p s : / / g r o u p s . g o o g l e . c o m / f o r u m / # ! s e a r c h i n / d i s c u s s - w e b r t c / o b j c $ 2 0 c r a s h $ 2 0 d e a l l o c % 7 C s o r t : r e l e v a n c e / d i s c u s s - w e b r t c / 7 D - v k 5 y L j n 8 / r B W 2 D 6 E W 4 G Y J
// T h e s e q u e n c e t o m a k e i t w o r k a p p e a r s t o b e
//
// [ c a p t u r e r s t o p ] ; / / I h a d t o a d d t h i s a s a m e t h o d t o R T C V i d e o C a p t u r e r
// [ l o c a l R e n d e r e r s t o p ] ;
// [ r e m o t e R e n d e r e r s t o p ] ;
// [ p e e r C o n n e c t i o n c l o s e ] ;
// a u d i o T r a c k i s a s t r o n g p r o p e r t y b e c a u s e w e n e e d a c c e s s t o i t t o m u t e / u n m u t e , b u t I w a s s e e i n g i t
// b e c o m e n i l w h e n i t w a s o n l y a w e a k p r o p e r t y . S o w e r e t a i n i t a n d m a n u a l l y n i l t h e r e f e r e n c e h e r e , b e c a u s e
// w e a r e l i k e l y t o c r a s h i f w e r e t a i n a n y p e e r c o n n e c t i o n p r o p e r t i e s w h e n t h e p e e r c o n n e c t i o n i s r e l e a s e d
Logger . debug ( " \( TAG ) in \( #function ) " )
audioTrack = nil
localVideoTrack = nil
remoteVideoTrack = nil
dataChannel = nil
audioSender = nil
videoSender = nil
peerConnection . delegate = nil
peerConnection . close ( )
}
// MARK: - D a t a C h a n n e l
// MARK: - D a t a C h a n n e l
public func sendDataChannelMessage ( data : Data ) -> Bool {
public func sendDataChannelMessage ( data : Data ) -> Bool {
AssertIsOnMainThread ( )
var result = false
var result = false
PeerConnectionClient . signalingQueue . sync {
PeerConnectionClient . signalingQueue . sync {
assertOnSignalingQueue ( )
guard let dataChannel = self . dataChannel else {
guard let dataChannel = self . dataChannel else {
Logger . error ( " \( self . TAG ) in \( #function ) ignoring sending \( data ) for nil dataChannel " )
Logger . error ( " \( self . TAG ) in \( #function ) ignoring sending \( data ) for nil dataChannel " )
result = false
result = false
@ -416,7 +423,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/* * 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 . */
/* * 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 . */
internal func dataChannelDidChangeState ( _ dataChannel : RTCDataChannel ) {
internal func dataChannelDidChangeState ( _ dataChannel : RTCDataChannel ) {
Logger . debug ( " \( self . TAG ) dataChannelDidChangeState: \( dataChannel ) " )
Logger . debug ( " \( TAG ) dataChannelDidChangeState: \( dataChannel ) " )
}
}
/* * 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 . */
/* * 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 . */
@ -440,14 +447,14 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/* * T h e d a t a c h a n n e l ' s | b u f f e r e d A m o u n t | c h a n g e d . */
/* * T h e d a t a c h a n n e l ' s | b u f f e r e d A m o u n t | c h a n g e d . */
internal func dataChannel ( _ dataChannel : RTCDataChannel , didChangeBufferedAmount amount : UInt64 ) {
internal func dataChannel ( _ dataChannel : RTCDataChannel , didChangeBufferedAmount amount : UInt64 ) {
Logger . debug ( " \( self . TAG ) didChangeBufferedAmount: \( amount ) " )
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
// 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 . */
/* * 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 . */
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didChange stateChanged : RTCSignalingState ) {
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didChange stateChanged : RTCSignalingState ) {
Logger . debug ( " \( self . TAG ) didChange signalingState: \( stateChanged . debugDescription ) " )
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 . */
/* * 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 . */
@ -469,12 +476,12 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/* * 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 . */
/* * 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 . */
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didRemove stream : RTCMediaStream ) {
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didRemove stream : RTCMediaStream ) {
Logger . debug ( " \( self . TAG ) didRemove Stream: \( stream ) " )
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 . */
/* * 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 . */
internal func peerConnectionShouldNegotiate ( _ peerConnection : RTCPeerConnection ) {
internal func peerConnectionShouldNegotiate ( _ peerConnection : RTCPeerConnection ) {
Logger . debug ( " \( self . TAG ) shouldNegotiate " )
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 . */
/* * 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 . */
@ -505,7 +512,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/* * 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 . */
/* * 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 . */
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didChange newState : RTCIceGatheringState ) {
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didChange newState : RTCIceGatheringState ) {
Logger . debug ( " \( self . TAG ) didChange IceGatheringState: \( newState . debugDescription ) " )
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 . */
/* * 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 . */
@ -522,7 +529,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/* * 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 . */
/* * 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 . */
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didRemove candidates : [ RTCIceCandidate ] ) {
internal func peerConnection ( _ peerConnection : RTCPeerConnection , didRemove candidates : [ RTCIceCandidate ] ) {
Logger . debug ( " \( self . TAG ) didRemove IceCandidates: \( candidates ) " )
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 . */
/* * 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 . */