diff --git a/Package.swift b/Package.swift index 1cb1d51..4c0b43c 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( - name: "webrtc-swift", + name: "bandwidth-webrtc", platforms: [.iOS(.v13), .macOS(.v10_15)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. @@ -15,7 +15,7 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/Bandwidth/json-rpc-websockets.git", .branch("main")), + .package(url: "https://github.com/Bandwidth/json-rpc-websockets.git", .upToNextMajor(from: "0.1.0")), .package(url: "https://github.com/alexpiezo/WebRTC.git", .upToNextMajor(from: "1.1.31567")) ], targets: [ diff --git a/Sources/BandwidthWebRTC/RTCBandwidthConnection.swift b/Sources/BandwidthWebRTC/Connection.swift similarity index 56% rename from Sources/BandwidthWebRTC/RTCBandwidthConnection.swift rename to Sources/BandwidthWebRTC/Connection.swift index 98b7edb..1245828 100644 --- a/Sources/BandwidthWebRTC/RTCBandwidthConnection.swift +++ b/Sources/BandwidthWebRTC/Connection.swift @@ -1,5 +1,5 @@ // -// RTCBandwidthConnection.swift +// Connection.swift // // // Created by Michael Hamer on 1/8/20. @@ -8,16 +8,18 @@ import Foundation import WebRTC -public class RTCBandwidthConnection { - let endpointId: String +class Connection { let peerConnection: RTCPeerConnection + let endpointId: String + let participantId: String + let mediaTypes: [MediaType] let alias: String? - let participantId: String? - init(endpointId: String, peerConnection: RTCPeerConnection, alias: String?, participantId: String?) { - self.endpointId = endpointId + init(peerConnection: RTCPeerConnection, endpointId: String, participantId: String, mediaTypes: [MediaType], alias: String?) { self.peerConnection = peerConnection - self.alias = alias + self.endpointId = endpointId self.participantId = participantId + self.mediaTypes = mediaTypes + self.alias = alias } } diff --git a/Sources/BandwidthWebRTC/PeerConnectionState.swift b/Sources/BandwidthWebRTC/PeerConnectionState.swift deleted file mode 100644 index db30575..0000000 --- a/Sources/BandwidthWebRTC/PeerConnectionState.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// PeerConnectionState.swift -// -// -// Created by Michael Hamer on 1/11/21. -// - -import Foundation - -public enum PeerConnectionState { - case new - case connecting - case connected - case disconnected - case failed - case closed -} diff --git a/Sources/BandwidthWebRTC/RTCBandwidth.swift b/Sources/BandwidthWebRTC/RTCBandwidth.swift index 2e32193..a1e42ef 100644 --- a/Sources/BandwidthWebRTC/RTCBandwidth.swift +++ b/Sources/BandwidthWebRTC/RTCBandwidth.swift @@ -9,7 +9,7 @@ import Foundation import WebRTC public protocol RTCBandwidthDelegate { - func bandwidth(_ bandwidth: RTCBandwidth, streamAvailableAt connection: RTCBandwidthConnection, stream: RTCMediaStream?) + func bandwidth(_ bandwidth: RTCBandwidth, streamAvailableAt endpointId: String, participantId: String, alias: String?, mediaTypes: [MediaType], mediaStream: RTCMediaStream?) func bandwidth(_ bandwidth: RTCBandwidth, streamUnavailableAt endpointId: String) } @@ -32,8 +32,8 @@ public class RTCBandwidth: NSObject { private let mediaConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: ["DtlsSrtpKeyAgreement": kRTCMediaConstraintsValueTrue]) - private var localConnections = [RTCBandwidthConnection]() - private var remoteConnections = [RTCBandwidthConnection]() + private var localConnections = [Connection]() + private var remoteConnections = [Connection]() #if os(iOS) private let audioSession = RTCAudioSession.sharedInstance() @@ -62,6 +62,10 @@ public class RTCBandwidth: NSObject { } } + public func disconnect() { + signaling?.disconnect() + } + public func publish(audio: Bool, video: Bool, alias: String?, completion: @escaping () -> Void) { var mediaTypes = [MediaType]() @@ -84,18 +88,27 @@ public class RTCBandwidth: NSObject { self.createMediaSenders(peerConnection: peerConnection, audio: audio, video: video) - let localConnection = RTCBandwidthConnection(endpointId: result.endpointId, peerConnection: peerConnection, alias: alias, participantId: nil) + let localConnection = Connection(peerConnection: peerConnection, endpointId: result.endpointId, participantId: result.participantId, mediaTypes: mediaTypes, alias: alias) self.localConnections.append(localConnection) self.negotiateSDP(endpointId: result.endpointId, direction: result.direction, mediaTypes: result.mediaTypes, for: peerConnection) { - DispatchQueue.main.async { - completion() - } + completion() } } } } + public func unpublish(endpointId: String) { + signaling?.unpublish(endpointId: endpointId) { result in + + } + + if let index = localConnections.firstIndex(where: { $0.endpointId == endpointId }) { + localConnections[index].peerConnection.close() + localConnections.remove(at: index) + } + } + // MARK: Media public func captureLocalVideo(renderer: RTCVideoRenderer) { @@ -197,7 +210,7 @@ public class RTCBandwidth: NSObject { return videoTrack } - private func negotiateSDP(endpointId: String, direction: String, mediaTypes: [String], for peerConnection: RTCPeerConnection, completion: @escaping () -> Void) { + private func negotiateSDP(endpointId: String, direction: String, mediaTypes: [MediaType], for peerConnection: RTCPeerConnection, completion: @escaping () -> Void) { debugPrint(direction) var mandatoryConstraints = [ @@ -206,14 +219,14 @@ public class RTCBandwidth: NSObject { ] if direction.contains("recv") { - mandatoryConstraints[kRTCMediaConstraintsOfferToReceiveAudio] = mediaTypes.contains("AUDIO") ? kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse - mandatoryConstraints[kRTCMediaConstraintsOfferToReceiveVideo] = mediaTypes.contains("VIDEO") ? kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse + mandatoryConstraints[kRTCMediaConstraintsOfferToReceiveAudio] = mediaTypes.contains(.audio) ? kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse + mandatoryConstraints[kRTCMediaConstraintsOfferToReceiveVideo] = mediaTypes.contains(.video) ? kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse } let constraints = RTCMediaConstraints(mandatoryConstraints: mandatoryConstraints, optionalConstraints: nil) peerConnection.offer(for: constraints) { offer, error in - DispatchQueue.main.async { +// DispatchQueue.main.async { if let error = error { print(error.localizedDescription) } @@ -228,7 +241,7 @@ public class RTCBandwidth: NSObject { } peerConnection.setLocalDescription(offer) { error in - DispatchQueue.main.async { +// DispatchQueue.main.async { if let error = error { debugPrint(error.localizedDescription) } @@ -242,21 +255,28 @@ public class RTCBandwidth: NSObject { completion() } - } +// } } } - } +// } } } private func handleSDPNeededEvent(parameters: SDPNeededParameters) { - let peerConnection = RTCBandwidth.factory.peerConnection(with: configuration, constraints: mediaConstraints, delegate: self) - let remoteConnection = RTCBandwidthConnection(endpointId: parameters.endpointId, peerConnection: peerConnection, alias: parameters.alias, participantId: parameters.participantId) + let remotePeerConnection = RTCBandwidth.factory.peerConnection(with: configuration, constraints: mediaConstraints, delegate: self) + + let remoteConnection = Connection( + peerConnection: remotePeerConnection, + endpointId: parameters.endpointId, + participantId: parameters.participantId, + mediaTypes: parameters.mediaTypes, + alias: parameters.alias + ) remoteConnections.append(remoteConnection) - negotiateSDP(endpointId: parameters.endpointId, direction: parameters.direction, mediaTypes: parameters.mediaTypes, for: peerConnection) { - + negotiateSDP(endpointId: parameters.endpointId, direction: parameters.direction, mediaTypes: parameters.mediaTypes, for: remotePeerConnection) { + } } @@ -288,11 +308,16 @@ extension RTCBandwidth: RTCPeerConnectionDelegate { public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd rtpReceiver: RTCRtpReceiver, streams mediaStreams: [RTCMediaStream]) { debugPrint("peerConnection didAdd rtpReceiver: streams media Streams:") - guard let connection = remoteConnections.first(where: { $0.peerConnection == peerConnection }) else { return } - - DispatchQueue.main.async { - self.delegate?.bandwidth(self, streamAvailableAt: connection, stream: mediaStreams.first) - } + guard let remoteConnection = remoteConnections.first(where: { $0.peerConnection == peerConnection }) else { return } + + self.delegate?.bandwidth( + self, + streamAvailableAt: remoteConnection.endpointId, + participantId: remoteConnection.participantId, + alias: remoteConnection.alias, + mediaTypes: remoteConnection.mediaTypes, + mediaStream: mediaStreams.first + ) } public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) { @@ -318,11 +343,30 @@ extension RTCBandwidth: RTCPeerConnectionDelegate { public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) { debugPrint("peerConnection didGenerate candidate: RTCIceCandidate") - guard let endpointId = remoteConnections.first(where: { $0.peerConnection == peerConnection })?.endpointId else { + guard let remoteConnection = remoteConnections.first(where: { $0.peerConnection == peerConnection }) else { return } - signaling?.sendIceCandidate(endpointId: endpointId, candidate: candidate) + signaling?.sendIceCandidate( + endpointId: remoteConnection.endpointId, + sdp: candidate.sdp, + sdpMLineIndex: Int(candidate.sdpMLineIndex), + sdpMid: candidate.sdpMid ?? "" + ) { _ in + + } + } + + public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCPeerConnectionState) { + print("peerConnection didChange newState: \(newState)") + + if [.disconnected, .failed].contains(newState) { + guard let remoteConnection = remoteConnections.first(where: { $0.peerConnection == peerConnection }) else { + return + } + + delegate?.bandwidth(self, streamUnavailableAt: remoteConnection.endpointId) + } } public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) { diff --git a/Sources/BandwidthWebRTC/Signaling/RPC/LeaveParameters.swift b/Sources/BandwidthWebRTC/Signaling/RPC/LeaveParameters.swift new file mode 100644 index 0000000..91db7fc --- /dev/null +++ b/Sources/BandwidthWebRTC/Signaling/RPC/LeaveParameters.swift @@ -0,0 +1,12 @@ +// +// LeaveParameters.swift +// +// +// Created by Michael Hamer on 1/28/21. +// + +import Foundation + +struct LeaveParameters: Codable { + +} diff --git a/Sources/BandwidthWebRTC/Signaling/RPC/RequestToPublishResult.swift b/Sources/BandwidthWebRTC/Signaling/RPC/RequestToPublishResult.swift index 1ee70d9..809624a 100644 --- a/Sources/BandwidthWebRTC/Signaling/RPC/RequestToPublishResult.swift +++ b/Sources/BandwidthWebRTC/Signaling/RPC/RequestToPublishResult.swift @@ -9,6 +9,7 @@ import Foundation struct RequestToPublishResult: Decodable { let endpointId: String - let mediaTypes: [String] + let participantId: String + let mediaTypes: [MediaType] let direction: String } diff --git a/Sources/BandwidthWebRTC/Signaling/RPC/SDPNeededParameters.swift b/Sources/BandwidthWebRTC/Signaling/RPC/SDPNeededParameters.swift index 67f5cc8..4121ea7 100644 --- a/Sources/BandwidthWebRTC/Signaling/RPC/SDPNeededParameters.swift +++ b/Sources/BandwidthWebRTC/Signaling/RPC/SDPNeededParameters.swift @@ -11,7 +11,7 @@ struct SDPNeededParameters: Codable { let alias: String let direction: String let endpointId: String - let mediaTypes: [String] + let mediaTypes: [MediaType] let participantId: String // let streamProperties: Any? // TODO: What should this do? } diff --git a/Sources/BandwidthWebRTC/Signaling/Signaling.swift b/Sources/BandwidthWebRTC/Signaling/Signaling.swift index f3a1320..6e7c860 100644 --- a/Sources/BandwidthWebRTC/Signaling/Signaling.swift +++ b/Sources/BandwidthWebRTC/Signaling/Signaling.swift @@ -7,7 +7,6 @@ import Foundation import JSONRPCWebSockets -import WebRTC enum SignalingMethod: String { case addICECandidate = "addIceCandidate" @@ -17,6 +16,7 @@ enum SignalingMethod: String { case sdpNeeded case setMediaPreferences case unpublish + case leave } protocol SignalingDelegate { @@ -64,6 +64,24 @@ class Signaling { } } + func disconnect() { + let leaveParameters = LeaveParameters() + client.notify(method: SignalingMethod.leave.rawValue, parameters: leaveParameters) { _ in + + } + + client.disconnect { + + } + } + + func unpublish(endpointId: String, completion: @escaping (Result<(), Error>) -> Void) { + let unpublishParameters = UnpublishParameters(endpointId: endpointId) + client.notify(method: SignalingMethod.unpublish.rawValue, parameters: unpublishParameters) { result in + completion(result) + } + } + func setMediaPreferences(protocol: String, aggregationType: String, sendReceive: Bool, completion: @escaping (SetMediaPreferencesResult?) -> Void) { let parameters = SetMediaPreferencesParameters(protocol: `protocol`, aggregationType: aggregationType, sendReceive: sendReceive) do { @@ -94,17 +112,16 @@ class Signaling { } } - func sendIceCandidate(endpointId: String, candidate: RTCIceCandidate) { - let parameters = AddICECandidateSendParameters( + func sendIceCandidate(endpointId: String, sdp: String, sdpMLineIndex: Int, sdpMid: String, completion: @escaping (Result<(), Error>) -> Void) { + let addICECandidateParameters = AddICECandidateSendParameters( endpointId: endpointId, - candidate: candidate.sdp, - sdpMLineIndex: Int(candidate.sdpMLineIndex), - sdpMid: candidate.sdpMid ?? "") + candidate: sdp, + sdpMLineIndex: sdpMLineIndex, + sdpMid: sdpMid + ) - try? client.notify(method: "addIceCandidate", parameters: parameters) { error in - if let error = error { - debugPrint(error.localizedDescription) - } + client.notify(method: SignalingMethod.addICECandidate.rawValue, parameters: addICECandidateParameters) { result in + completion(result) } } }