Fix crashes while deallocating PeerConnectionClient.

pull/1/head
Matthew Chen 7 years ago
parent 08affb4400
commit fff9f74a05

@ -66,20 +66,12 @@ protocol PeerConnectionClientDelegate: class {
func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, didUpdateRemote videoTrack: RTCVideoTrack?) func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, didUpdateRemote videoTrack: RTCVideoTrack?)
} }
/**
* `PeerConnectionClient` is our interface to WebRTC.
*
* 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)
*/
class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelDelegate {
// In Swift (at least in Swift v3.3), weak variables aren't thread safe. It // In Swift (at least in Swift v3.3), weak variables aren't thread safe. It
// isn't safe to resolve/acquire/lock a weak reference into a strong reference // isn't safe to resolve/acquire/lock a weak reference into a strong reference
// while the instance might be being deallocated on another thread. // while the instance might be being deallocated on another thread.
// //
// AtomicHandle provides thread-safe access to a strong reference. // PeerConnectionProxy provides thread-safe access to a strong reference.
// PeerConnectionClient has an AtomicHandle to itself that its many async blocks // PeerConnectionClient has an PeerConnectionProxy to itself that its many async blocks
// (which run on more than one thread) can use to safely try to acquire a strong // (which run on more than one thread) can use to safely try to acquire a strong
// reference to the PeerConnectionClient. In ARC we'd normally, we'd avoid // reference to the PeerConnectionClient. In ARC we'd normally, we'd avoid
// having an instance retain a strong reference to itself to avoid retain // having an instance retain a strong reference to itself to avoid retain
@ -88,8 +80,9 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
// [PeerConnectionClient terminate] when it is done with a PeerConnectionClient // [PeerConnectionClient terminate] when it is done with a PeerConnectionClient
// instance, so terminate is a reliable place where we can break the retain cycle. // instance, so terminate is a reliable place where we can break the retain cycle.
// //
// TODO: This should be fixed in Swift 4. To verify, remove AtomicHandle and // This should be fixed in Swift 4, but it isn't.
// use [weak self] as usual. Test using the following scenarios: //
// To test using the following scenarios:
// //
// * Alice and Bob place simultaneous calls to each other. Both should get busy. // * Alice and Bob place simultaneous calls to each other. Both should get busy.
// Repeat 10-20x. Then verify that they can connect a call by having just one // Repeat 10-20x. Then verify that they can connect a call by having just one
@ -97,16 +90,21 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
// * Alice or Bob (randomly alternating) calls the other. Recipient (randomly) // * Alice or Bob (randomly alternating) calls the other. Recipient (randomly)
// accepts call or hangs up. If accepted, Alice or Bob (randomly) hangs up. // accepts call or hangs up. If accepted, Alice or Bob (randomly) hangs up.
// Repeat immediately, as fast as you can, 10-20x. // Repeat immediately, as fast as you can, 10-20x.
private class AtomicHandle<ValueType> : NSObject { class PeerConnectionProxy: NSObject, RTCPeerConnectionDelegate, RTCDataChannelDelegate {
var value: ValueType?
private var value: PeerConnectionClient?
deinit {
Logger.info("[PeerConnectionProxy] deinit")
}
func set(value: ValueType) { func set(value: PeerConnectionClient) {
objc_sync_enter(self) objc_sync_enter(self)
self.value = value self.value = value
objc_sync_exit(self) objc_sync_exit(self)
} }
func get() -> ValueType? { func get() -> PeerConnectionClient? {
objc_sync_enter(self) objc_sync_enter(self)
let result = value let result = value
objc_sync_exit(self) objc_sync_exit(self)
@ -120,8 +118,68 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
value = nil value = nil
objc_sync_exit(self) objc_sync_exit(self)
} }
// MARK: - RTCPeerConnectionDelegate
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
self.get()?.peerConnection(peerConnection, didChange: stateChanged)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
self.get()?.peerConnection(peerConnection, didAdd: stream)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
self.get()?.peerConnection(peerConnection, didRemove: stream)
}
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
self.get()?.peerConnectionShouldNegotiate(peerConnection)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
self.get()?.peerConnection(peerConnection, didChange: newState)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
self.get()?.peerConnection(peerConnection, didChange: newState)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
self.get()?.peerConnection(peerConnection, didGenerate: candidate)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
self.get()?.peerConnection(peerConnection, didRemove: candidates)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
self.get()?.peerConnection(peerConnection, didOpen: dataChannel)
}
// MARK: - RTCDataChannelDelegate
public func dataChannelDidChangeState(_ dataChannel: RTCDataChannel) {
self.get()?.dataChannelDidChangeState(dataChannel)
}
public func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) {
self.get()?.dataChannel(dataChannel, didReceiveMessageWith: buffer)
}
public func dataChannel(_ dataChannel: RTCDataChannel, didChangeBufferedAmount amount: UInt64) {
self.get()?.dataChannel(dataChannel, didChangeBufferedAmount: amount)
}
} }
/**
* `PeerConnectionClient` is our interface to WebRTC.
*
* 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)
*/
class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelDelegate {
enum Identifiers: String { enum Identifiers: String {
case mediaStream = "ARDAMS", case mediaStream = "ARDAMS",
videoTrack = "ARDAMSv0", videoTrack = "ARDAMSv0",
@ -171,7 +229,8 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
private var remoteVideoTrack: RTCVideoTrack? private var remoteVideoTrack: RTCVideoTrack?
private var cameraConstraints: RTCMediaConstraints private var cameraConstraints: RTCMediaConstraints
private let handle: AtomicHandle<PeerConnectionClient> private let proxy = PeerConnectionProxy()
private static var expiredProxies = [PeerConnectionProxy]()
init(iceServers: [RTCIceServer], delegate: PeerConnectionClientDelegate, callDirection: CallDirection, useTurnOnly: Bool) { init(iceServers: [RTCIceServer], delegate: PeerConnectionClientDelegate, callDirection: CallDirection, useTurnOnly: Bool) {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
@ -196,15 +255,13 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
audioConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) audioConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
cameraConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) cameraConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
handle = AtomicHandle<PeerConnectionClient>()
super.init() super.init()
handle.set(value: self) proxy.set(value: self)
peerConnection = factory.peerConnection(with: configuration, peerConnection = factory.peerConnection(with: configuration,
constraints: connectionConstraints, constraints: connectionConstraints,
delegate: self) delegate: proxy)
createAudioSender() createAudioSender()
createVideoSender() createVideoSender()
@ -235,7 +292,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
configuration.isOrdered = true configuration.isOrdered = true
let dataChannel = peerConnection.dataChannel(forLabel: Identifiers.dataChannelSignaling.rawValue, let dataChannel = peerConnection.dataChannel(forLabel: Identifiers.dataChannelSignaling.rawValue,
configuration: configuration) configuration: configuration)
dataChannel.delegate = self dataChannel.delegate = proxy
assert(self.dataChannel == nil) assert(self.dataChannel == nil)
self.dataChannel = dataChannel self.dataChannel = dataChannel
@ -285,9 +342,9 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func setCameraSource(useBackCamera: Bool) { public func setCameraSource(useBackCamera: Bool) {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let localVideoSource = strongSelf.localVideoSource else { guard let localVideoSource = strongSelf.localVideoSource else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -305,16 +362,16 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func setLocalVideoEnabled(enabled: Bool) { public func setLocalVideoEnabled(enabled: Bool) {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
let completion = { [weak self] in let completion = {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let localVideoTrack = strongSelf.localVideoTrack else { return } guard let localVideoTrack = strongSelf.localVideoTrack else { return }
guard let strongDelegate = strongSelf.delegate else { return } guard let strongDelegate = strongSelf.delegate else { return }
strongDelegate.peerConnectionClient(strongSelf, didUpdateLocal: enabled ? localVideoTrack : nil) strongDelegate.peerConnectionClient(strongSelf, didUpdateLocal: enabled ? localVideoTrack : nil)
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard strongSelf.peerConnection != nil else { guard strongSelf.peerConnection != nil else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -372,9 +429,9 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func setAudioEnabled(enabled: Bool) { public func setAudioEnabled(enabled: Bool) {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard strongSelf.peerConnection != nil else { guard strongSelf.peerConnection != nil else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -400,10 +457,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func createOffer() -> Promise<HardenedRTCSessionDescription> { public func createOffer() -> Promise<HardenedRTCSessionDescription> {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
let (promise, fulfill, reject) = Promise<HardenedRTCSessionDescription>.pending() let (promise, fulfill, reject) = Promise<HardenedRTCSessionDescription>.pending()
let completion: ((RTCSessionDescription?, Error?) -> Void) = { [weak self] (sdp, error) in let completion: ((RTCSessionDescription?, Error?) -> Void) = { (sdp, error) in
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil)) reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
return return
} }
@ -428,8 +485,8 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
fulfill(HardenedRTCSessionDescription(rtcSessionDescription: sessionDescription)) fulfill(HardenedRTCSessionDescription(rtcSessionDescription: sessionDescription))
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil)) reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
return return
} }
@ -451,10 +508,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
} }
public func setLocalSessionDescriptionInternal(_ sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> { public func setLocalSessionDescriptionInternal(_ sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> {
let handle = self.handle let proxyCopy = self.proxy
let (promise, fulfill, reject) = Promise<Void>.pending() let (promise, fulfill, reject) = Promise<Void>.pending()
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil)) reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
return return
} }
@ -471,7 +528,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
if let error = error { if let error = error {
reject(error) reject(error)
} else { } else {
fulfill() fulfill(())
} }
}) })
} }
@ -480,10 +537,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func setLocalSessionDescription(_ sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> { public func setLocalSessionDescription(_ sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
let (promise, fulfill, reject) = Promise<Void>.pending() let (promise, fulfill, reject) = Promise<Void>.pending()
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil)) reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
return return
} }
@ -501,7 +558,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
reject(error) reject(error)
return return
} }
fulfill() fulfill(())
}) })
} }
@ -510,10 +567,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func negotiateSessionDescription(remoteDescription: RTCSessionDescription, constraints: RTCMediaConstraints) -> Promise<HardenedRTCSessionDescription> { public func negotiateSessionDescription(remoteDescription: RTCSessionDescription, constraints: RTCMediaConstraints) -> Promise<HardenedRTCSessionDescription> {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
return setRemoteSessionDescription(remoteDescription) return setRemoteSessionDescription(remoteDescription)
.then(on: PeerConnectionClient.signalingQueue) { [weak self] in .then(on: PeerConnectionClient.signalingQueue) {
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
return Promise(error: NSError(domain: "Obsolete client", code: 0, userInfo: nil)) return Promise(error: NSError(domain: "Obsolete client", code: 0, userInfo: nil))
} }
return strongSelf.negotiateAnswerSessionDescription(constraints: constraints) return strongSelf.negotiateAnswerSessionDescription(constraints: constraints)
@ -522,10 +579,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func setRemoteSessionDescription(_ sessionDescription: RTCSessionDescription) -> Promise<Void> { public func setRemoteSessionDescription(_ sessionDescription: RTCSessionDescription) -> Promise<Void> {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
let (promise, fulfill, reject) = Promise<Void>.pending() let (promise, fulfill, reject) = Promise<Void>.pending()
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil)) reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
return return
} }
@ -542,7 +599,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
reject(error) reject(error)
return return
} }
fulfill() fulfill(())
}) })
} }
return promise return promise
@ -550,10 +607,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
private func negotiateAnswerSessionDescription(constraints: RTCMediaConstraints) -> Promise<HardenedRTCSessionDescription> { private func negotiateAnswerSessionDescription(constraints: RTCMediaConstraints) -> Promise<HardenedRTCSessionDescription> {
assertOnSignalingQueue() assertOnSignalingQueue()
let handle = self.handle let proxyCopy = self.proxy
let (promise, fulfill, reject) = Promise<HardenedRTCSessionDescription>.pending() let (promise, fulfill, reject) = Promise<HardenedRTCSessionDescription>.pending()
let completion: ((RTCSessionDescription?, Error?) -> Void) = { [weak self] (sdp, error) in let completion: ((RTCSessionDescription?, Error?) -> Void) = { (sdp, error) in
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil)) reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
return return
} }
@ -585,8 +642,8 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
} }
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { guard let strongSelf = proxyCopy.get() else {
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil)) reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
return return
} }
@ -610,9 +667,9 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
} }
public func addRemoteIceCandidate(_ candidate: RTCIceCandidate) { public func addRemoteIceCandidate(_ candidate: RTCIceCandidate) {
let handle = self.handle let proxyCopy = self.proxy
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let peerConnection = strongSelf.peerConnection else { guard let peerConnection = strongSelf.peerConnection else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -630,9 +687,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
// no delegate methods are called after terminate() returns. // no delegate methods are called after terminate() returns.
delegate = nil delegate = nil
// Clear the handle immediately so that enqueued work is aborted // Clear the proxy immediately so that enqueued work is aborted
// going forward. // going forward.
handle.clear() PeerConnectionClient.expiredProxies.append(proxy)
proxy.clear()
// Don't use [weak self]; we always want to perform terminateInternal(). // Don't use [weak self]; we always want to perform terminateInternal().
PeerConnectionClient.signalingQueue.async { PeerConnectionClient.signalingQueue.async {
@ -691,9 +749,9 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
public func sendDataChannelMessage(data: Data, description: String, isCritical: Bool) { public func sendDataChannelMessage(data: Data, description: String, isCritical: Bool) {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
let handle = self.handle let proxyCopy = self.proxy
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard strongSelf.peerConnection != nil else { guard strongSelf.peerConnection != nil else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client: \(description)") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client: \(description)")
@ -735,16 +793,16 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/** The data channel successfully received a data buffer. */ /** The data channel successfully received a data buffer. */
internal func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) { internal func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) {
let handle = self.handle let proxyCopy = self.proxy
let completion: (OWSWebRTCProtosData) -> Void = { [weak self] (dataChannelMessage) in let completion: (OWSWebRTCProtosData) -> Void = { (dataChannelMessage) in
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let strongDelegate = strongSelf.delegate else { return } guard let strongDelegate = strongSelf.delegate else { return }
strongDelegate.peerConnectionClient(strongSelf, received: dataChannelMessage) strongDelegate.peerConnectionClient(strongSelf, received: dataChannelMessage)
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard strongSelf.peerConnection != nil else { guard strongSelf.peerConnection != nil else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -777,10 +835,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/** Called when media is received on a new stream from remote peer. */ /** Called when media is received on a new stream from remote peer. */
internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didAdd stream: RTCMediaStream) { internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didAdd stream: RTCMediaStream) {
let handle = self.handle let proxyCopy = self.proxy
let completion: (RTCVideoTrack) -> Void = { [weak self] (remoteVideoTrack) in let completion: (RTCVideoTrack) -> Void = { (remoteVideoTrack) in
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let strongDelegate = strongSelf.delegate else { return } guard let strongDelegate = strongSelf.delegate else { return }
// TODO: Consider checking for termination here. // TODO: Consider checking for termination here.
@ -788,8 +846,8 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
strongDelegate.peerConnectionClient(strongSelf, didUpdateRemote: remoteVideoTrack) strongDelegate.peerConnectionClient(strongSelf, didUpdateRemote: remoteVideoTrack)
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let peerConnection = strongSelf.peerConnection else { guard let peerConnection = strongSelf.peerConnection else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -825,28 +883,28 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/** Called any time the IceConnectionState changes. */ /** Called any time the IceConnectionState changes. */
internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didChange newState: RTCIceConnectionState) { internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
let handle = self.handle let proxyCopy = self.proxy
let connectedCompletion : () -> Void = { [weak self] in let connectedCompletion : () -> Void = {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let strongDelegate = strongSelf.delegate else { return } guard let strongDelegate = strongSelf.delegate else { return }
strongDelegate.peerConnectionClientIceConnected(strongSelf) strongDelegate.peerConnectionClientIceConnected(strongSelf)
} }
let failedCompletion : () -> Void = { [weak self] in let failedCompletion : () -> Void = {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let strongDelegate = strongSelf.delegate else { return } guard let strongDelegate = strongSelf.delegate else { return }
strongDelegate.peerConnectionClientIceFailed(strongSelf) strongDelegate.peerConnectionClientIceFailed(strongSelf)
} }
let disconnectedCompletion : () -> Void = { [weak self] in let disconnectedCompletion : () -> Void = {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let strongDelegate = strongSelf.delegate else { return } guard let strongDelegate = strongSelf.delegate else { return }
strongDelegate.peerConnectionClientIceDisconnected(strongSelf) strongDelegate.peerConnectionClientIceDisconnected(strongSelf)
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let peerConnection = strongSelf.peerConnection else { guard let peerConnection = strongSelf.peerConnection else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -879,16 +937,16 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/** New ice candidate has been found. */ /** New ice candidate has been found. */
internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) { internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
let handle = self.handle let proxyCopy = self.proxy
let completion: (RTCIceCandidate) -> Void = { [weak self] (candidate) in let completion: (RTCIceCandidate) -> Void = { (candidate) in
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let strongDelegate = strongSelf.delegate else { return } guard let strongDelegate = strongSelf.delegate else { return }
strongDelegate.peerConnectionClient(strongSelf, addedLocalIceCandidate: candidate) strongDelegate.peerConnectionClient(strongSelf, addedLocalIceCandidate: candidate)
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let peerConnection = strongSelf.peerConnection else { guard let peerConnection = strongSelf.peerConnection else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -911,17 +969,17 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
/** New data channel has been opened. */ /** New data channel has been opened. */
internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) { internal func peerConnection(_ peerConnectionParam: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
let handle = self.handle let proxyCopy = self.proxy
let completion: ([PendingDataChannelMessage]) -> Void = { [weak self] (pendingMessages) in let completion: ([PendingDataChannelMessage]) -> Void = { (pendingMessages) in
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
pendingMessages.forEach { message in pendingMessages.forEach { message in
strongSelf.sendDataChannelMessage(data: message.data, description: message.description, isCritical: message.isCritical) strongSelf.sendDataChannelMessage(data: message.data, description: message.description, isCritical: message.isCritical)
} }
} }
PeerConnectionClient.signalingQueue.async { [weak self] in PeerConnectionClient.signalingQueue.async {
guard let strongSelf = handle.get() else { return } guard let strongSelf = proxyCopy.get() else { return }
guard let peerConnection = strongSelf.peerConnection else { guard let peerConnection = strongSelf.peerConnection else {
Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client") Logger.debug("\(strongSelf.logTag) \(#function) Ignoring obsolete event in terminated client")
return return
@ -935,7 +993,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
owsFail("\(strongSelf.logTag) in \(#function) dataChannel unexpectedly set twice.") owsFail("\(strongSelf.logTag) in \(#function) dataChannel unexpectedly set twice.")
} }
strongSelf.dataChannel = dataChannel strongSelf.dataChannel = dataChannel
dataChannel.delegate = strongSelf dataChannel.delegate = strongSelf.proxy
let pendingMessages = strongSelf.pendingDataChannelMessages let pendingMessages = strongSelf.pendingDataChannelMessages
strongSelf.pendingDataChannelMessages = [] strongSelf.pendingDataChannelMessages = []

Loading…
Cancel
Save