From 1ad42547b2cd474f1f22533b394073d635fd7d5e Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 17 Aug 2021 16:02:20 +1000 Subject: [PATCH] Batch send ICE candidates --- Session/Calls/CallVCV2.swift | 1 + Session/Meta/AppDelegate.swift | 2 +- .../Calls/WebRTCWrapper+MessageHandling.swift | 5 +- SessionMessagingKit/Calls/WebRTCWrapper.swift | 39 ++++++--- .../Control Messages/CallMessage.swift | 62 ++++++------- .../Protos/Generated/SNProto.swift | 87 +++++++++---------- .../Protos/Generated/SessionProtos.pb.swift | 70 +++++---------- .../Protos/SessionProtos.proto | 13 +-- .../MessageReceiver+Handling.swift | 16 +++- SessionUtilitiesKit/Networking/HTTP.swift | 5 +- 10 files changed, 151 insertions(+), 149 deletions(-) diff --git a/Session/Calls/CallVCV2.swift b/Session/Calls/CallVCV2.swift index b40d71d99..40b07ef80 100644 --- a/Session/Calls/CallVCV2.swift +++ b/Session/Calls/CallVCV2.swift @@ -37,6 +37,7 @@ final class CallVCV2 : UIViewController, WebRTCWrapperDelegate { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .black WebRTCWrapper.current = webRTCWrapper setUpViewHierarchy() cameraManager.prepare() diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index c9ea18bdc..6186fd4cd 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -7,7 +7,7 @@ extension AppDelegate { func setUpCallHandling() { MessageReceiver.handleOfferCallMessage = { message in DispatchQueue.main.async { - let sdp = RTCSessionDescription(type: .offer, sdp: message.sdp!) + let sdp = RTCSessionDescription(type: .offer, sdp: message.sdps![0]) guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // TODO: Handle more gracefully let alert = UIAlertController(title: "Incoming Call", message: nil, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Accept", style: .default, handler: { _ in diff --git a/SessionMessagingKit/Calls/WebRTCWrapper+MessageHandling.swift b/SessionMessagingKit/Calls/WebRTCWrapper+MessageHandling.swift index 88f60a24d..57069ab42 100644 --- a/SessionMessagingKit/Calls/WebRTCWrapper+MessageHandling.swift +++ b/SessionMessagingKit/Calls/WebRTCWrapper+MessageHandling.swift @@ -2,9 +2,9 @@ import WebRTC extension WebRTCWrapper { - public func handleICECandidate(_ candidate: RTCIceCandidate) { + public func handleICECandidates(_ candidate: [RTCIceCandidate]) { print("[Calls] Received ICE candidate message.") - peerConnection.add(candidate) + candidate.forEach { peerConnection.add($0) } } public func handleRemoteSDP(_ sdp: RTCSessionDescription, from sessionID: String) { @@ -15,7 +15,6 @@ extension WebRTCWrapper { } else { guard let self = self, sdp.type == .offer, self.peerConnection.localDescription == nil else { return } - // Automatically answer the call Storage.write { transaction in self.sendAnswer(to: sessionID, using: transaction).retainUntilComplete() } diff --git a/SessionMessagingKit/Calls/WebRTCWrapper.swift b/SessionMessagingKit/Calls/WebRTCWrapper.swift index 49cd939bb..116a0d626 100644 --- a/SessionMessagingKit/Calls/WebRTCWrapper.swift +++ b/SessionMessagingKit/Calls/WebRTCWrapper.swift @@ -9,6 +9,8 @@ public protocol WebRTCWrapperDelegate : AnyObject { public final class WebRTCWrapper : NSObject, RTCPeerConnectionDelegate { public weak var delegate: WebRTCWrapperDelegate? private let contactSessionID: String + private var queuedICECandidates: [RTCIceCandidate] = [] + private var iceCandidateSendTimer: Timer? private let defaultICEServers = [ "stun:stun.l.google.com:19302", @@ -101,7 +103,7 @@ public final class WebRTCWrapper : NSObject, RTCPeerConnectionDelegate { audioSession.unlockForConfiguration() } - // MARK: Call Management + // MARK: Signaling public func sendOffer(to sessionID: String, using transaction: YapDatabaseReadWriteTransaction) -> Promise { print("[Calls] Initiating call.") guard let thread = TSContactThread.fetch(for: sessionID, using: transaction) else { return Promise(error: Error.noThread) } @@ -120,7 +122,7 @@ public final class WebRTCWrapper : NSObject, RTCPeerConnectionDelegate { DispatchQueue.main.async { let message = CallMessage() message.kind = .offer - message.sdp = sdp.sdp + message.sdps = [ sdp.sdp ] MessageSender.send(message, in: thread, using: transaction) seal.fulfill(()) } @@ -147,7 +149,7 @@ public final class WebRTCWrapper : NSObject, RTCPeerConnectionDelegate { DispatchQueue.main.async { let message = CallMessage() message.kind = .answer - message.sdp = sdp.sdp + message.sdps = [ sdp.sdp ] MessageSender.send(message, in: thread, using: transaction) seal.fulfill(()) } @@ -156,6 +158,29 @@ public final class WebRTCWrapper : NSObject, RTCPeerConnectionDelegate { return promise } + private func queueICECandidateForSending(_ candidate: RTCIceCandidate) { + queuedICECandidates.append(candidate) + iceCandidateSendTimer?.invalidate() + iceCandidateSendTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in + self.sendICECandidates() + } + } + + private func sendICECandidates() { + Storage.write { transaction in + let candidates = self.queuedICECandidates + guard let thread = TSContactThread.fetch(for: self.contactSessionID, using: transaction) else { return } + let message = CallMessage() + let sdps = candidates.map { $0.sdp } + let sdpMLineIndexes = candidates.map { UInt32($0.sdpMLineIndex) } + let sdpMids = candidates.map { $0.sdpMid! } + message.kind = .iceCandidates(sdpMLineIndexes: sdpMLineIndexes, sdpMids: sdpMids) + message.sdps = sdps + self.queuedICECandidates.removeAll() + MessageSender.send(message, in: thread, using: transaction) + } + } + public func dropConnection() { peerConnection.close() } @@ -187,13 +212,7 @@ public final class WebRTCWrapper : NSObject, RTCPeerConnectionDelegate { public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) { print("[Calls] ICE candidate generated.") - Storage.write { transaction in - guard let thread = TSContactThread.fetch(for: self.contactSessionID, using: transaction) else { return } - let message = CallMessage() - message.kind = .iceCandidate(sdpMLineIndex: UInt32(candidate.sdpMLineIndex), sdpMid: candidate.sdpMid!) - message.sdp = candidate.sdp - MessageSender.send(message, in: thread, using: transaction) - } + queueICECandidateForSending(candidate) } public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) { diff --git a/SessionMessagingKit/Messages/Control Messages/CallMessage.swift b/SessionMessagingKit/Messages/Control Messages/CallMessage.swift index b4fed52a8..248dcb904 100644 --- a/SessionMessagingKit/Messages/Control Messages/CallMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/CallMessage.swift @@ -1,25 +1,27 @@ import WebRTC +// NOTE: Multiple ICE candidates may be batched together for performance + /// See https://developer.mozilla.org/en-US/docs/Web/API/RTCSessionDescription for more information. @objc(SNCallMessage) public final class CallMessage : ControlMessage { public var kind: Kind? /// See https://developer.mozilla.org/en-US/docs/Glossary/SDP for more information. - public var sdp: String? + public var sdps: [String]? // MARK: Kind public enum Kind : Codable, CustomStringConvertible { case offer case answer case provisionalAnswer - case iceCandidate(sdpMLineIndex: UInt32, sdpMid: String) + case iceCandidates(sdpMLineIndexes: [UInt32], sdpMids: [String]) public var description: String { switch self { case .offer: return "offer" case .answer: return "answer" case .provisionalAnswer: return "provisionalAnswer" - case .iceCandidate(_, _): return "iceCandidate" + case .iceCandidates(_, _): return "iceCandidates" } } } @@ -27,16 +29,17 @@ public final class CallMessage : ControlMessage { // MARK: Initialization public override init() { super.init() } - internal init(kind: Kind, sdp: String) { + internal init(kind: Kind, sdps: [String]) { super.init() self.kind = kind - self.sdp = sdp + self.sdps = sdps } // MARK: Validation public override var isValid: Bool { guard super.isValid else { return false } - return kind != nil && sdp != nil + guard let sdps = sdps, !sdps.isEmpty else { return false } + return kind != nil } // MARK: Coding @@ -47,13 +50,13 @@ public final class CallMessage : ControlMessage { case "offer": kind = .offer case "answer": kind = .answer case "provisionalAnswer": kind = .provisionalAnswer - case "iceCandidate": - guard let sdpMLineIndex = coder.decodeObject(forKey: "sdpMLineIndex") as? UInt32, - let sdpMid = coder.decodeObject(forKey: "sdpMid") as? String else { return nil } - kind = .iceCandidate(sdpMLineIndex: sdpMLineIndex, sdpMid: sdpMid) + case "iceCandidates": + guard let sdpMLineIndexes = coder.decodeObject(forKey: "sdpMLineIndexes") as? [UInt32], + let sdpMids = coder.decodeObject(forKey: "sdpMids") as? [String] else { return nil } + kind = .iceCandidates(sdpMLineIndexes: sdpMLineIndexes, sdpMids: sdpMids) default: preconditionFailure() } - if let sdp = coder.decodeObject(forKey: "sdp") as! String? { self.sdp = sdp } + if let sdps = coder.decodeObject(forKey: "sdps") as! [String]? { self.sdps = sdps } } public override func encode(with coder: NSCoder) { @@ -62,13 +65,13 @@ public final class CallMessage : ControlMessage { case .offer: coder.encode("offer", forKey: "kind") case .answer: coder.encode("answer", forKey: "kind") case .provisionalAnswer: coder.encode("provisionalAnswer", forKey: "kind") - case let .iceCandidate(sdpMLineIndex, sdpMid): - coder.encode("iceCandidate", forKey: "kind") - coder.encode(sdpMLineIndex, forKey: "sdpMLineIndex") - coder.encode(sdpMid, forKey: "sdpMid") + case let .iceCandidates(sdpMLineIndexes, sdpMids): + coder.encode("iceCandidates", forKey: "kind") + coder.encode(sdpMLineIndexes, forKey: "sdpMLineIndexes") + coder.encode(sdpMids, forKey: "sdpMids") default: preconditionFailure() } - coder.encode(sdp, forKey: "sdp") + coder.encode(sdps, forKey: "sdps") } // MARK: Proto Conversion @@ -79,17 +82,17 @@ public final class CallMessage : ControlMessage { case .offer: kind = .offer case .answer: kind = .answer case .provisionalAnswer: kind = .provisionalAnswer - case .iceCandidate: - let sdpMLineIndex = callMessageProto.sdpMlineIndex - guard let sdpMid = callMessageProto.sdpMid else { return nil } - kind = .iceCandidate(sdpMLineIndex: sdpMLineIndex, sdpMid: sdpMid) + case .iceCandidates: + let sdpMLineIndexes = callMessageProto.sdpMlineIndexes + let sdpMids = callMessageProto.sdpMids + kind = .iceCandidates(sdpMLineIndexes: sdpMLineIndexes, sdpMids: sdpMids) } - let sdp = callMessageProto.sdp - return CallMessage(kind: kind, sdp: sdp) + let sdps = callMessageProto.sdps + return CallMessage(kind: kind, sdps: sdps) } public override func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? { - guard let kind = kind, let sdp = sdp else { + guard let kind = kind, let sdps = sdps, !sdps.isEmpty else { SNLog("Couldn't construct call message proto from: \(self).") return nil } @@ -101,12 +104,13 @@ public final class CallMessage : ControlMessage { case .offer: type = .offer case .answer: type = .answer case .provisionalAnswer: type = .provisionalAnswer - case .iceCandidate(_, _): type = .iceCandidate + case .iceCandidates(_, _): type = .iceCandidates } - let callMessageProto = SNProtoCallMessage.builder(type: type, sdp: sdp) - if case let .iceCandidate(sdpMLineIndex, sdpMid) = kind { - callMessageProto.setSdpMlineIndex(sdpMLineIndex) - callMessageProto.setSdpMid(sdpMid) + let callMessageProto = SNProtoCallMessage.builder(type: type) + callMessageProto.setSdps(sdps) + if case let .iceCandidates(sdpMLineIndexes, sdpMids) = kind { + callMessageProto.setSdpMlineIndexes(sdpMLineIndexes) + callMessageProto.setSdpMids(sdpMids) } let contentProto = SNProtoContent.builder() do { @@ -123,7 +127,7 @@ public final class CallMessage : ControlMessage { """ CallMessage( kind: \(kind?.description ?? "null"), - sdp: \(sdp ?? "null") + sdps: \(sdps?.description ?? "null") ) """ } diff --git a/SessionMessagingKit/Protos/Generated/SNProto.swift b/SessionMessagingKit/Protos/Generated/SNProto.swift index 9348b2524..134c63017 100644 --- a/SessionMessagingKit/Protos/Generated/SNProto.swift +++ b/SessionMessagingKit/Protos/Generated/SNProto.swift @@ -656,7 +656,7 @@ extension SNProtoContent.SNProtoContentBuilder { case offer = 1 case answer = 2 case provisionalAnswer = 3 - case iceCandidate = 4 + case iceCandidates = 4 } private class func SNProtoCallMessageTypeWrap(_ value: SessionProtos_CallMessage.TypeEnum) -> SNProtoCallMessageType { @@ -664,7 +664,7 @@ extension SNProtoContent.SNProtoContentBuilder { case .offer: return .offer case .answer: return .answer case .provisionalAnswer: return .provisionalAnswer - case .iceCandidate: return .iceCandidate + case .iceCandidates: return .iceCandidates } } @@ -673,25 +673,22 @@ extension SNProtoContent.SNProtoContentBuilder { case .offer: return .offer case .answer: return .answer case .provisionalAnswer: return .provisionalAnswer - case .iceCandidate: return .iceCandidate + case .iceCandidates: return .iceCandidates } } // MARK: - SNProtoCallMessageBuilder - @objc public class func builder(type: SNProtoCallMessageType, sdp: String) -> SNProtoCallMessageBuilder { - return SNProtoCallMessageBuilder(type: type, sdp: sdp) + @objc public class func builder(type: SNProtoCallMessageType) -> SNProtoCallMessageBuilder { + return SNProtoCallMessageBuilder(type: type) } // asBuilder() constructs a builder that reflects the proto's contents. @objc public func asBuilder() -> SNProtoCallMessageBuilder { - let builder = SNProtoCallMessageBuilder(type: type, sdp: sdp) - if hasSdpMlineIndex { - builder.setSdpMlineIndex(sdpMlineIndex) - } - if let _value = sdpMid { - builder.setSdpMid(_value) - } + let builder = SNProtoCallMessageBuilder(type: type) + builder.setSdps(sdps) + builder.setSdpMlineIndexes(sdpMlineIndexes) + builder.setSdpMids(sdpMids) return builder } @@ -701,27 +698,44 @@ extension SNProtoContent.SNProtoContentBuilder { @objc fileprivate override init() {} - @objc fileprivate init(type: SNProtoCallMessageType, sdp: String) { + @objc fileprivate init(type: SNProtoCallMessageType) { super.init() setType(type) - setSdp(sdp) } @objc public func setType(_ valueParam: SNProtoCallMessageType) { proto.type = SNProtoCallMessageTypeUnwrap(valueParam) } - @objc public func setSdp(_ valueParam: String) { - proto.sdp = valueParam + @objc public func addSdps(_ valueParam: String) { + var items = proto.sdps + items.append(valueParam) + proto.sdps = items + } + + @objc public func setSdps(_ wrappedItems: [String]) { + proto.sdps = wrappedItems + } + + @objc public func addSdpMlineIndexes(_ valueParam: UInt32) { + var items = proto.sdpMlineIndexes + items.append(valueParam) + proto.sdpMlineIndexes = items + } + + @objc public func setSdpMlineIndexes(_ wrappedItems: [UInt32]) { + proto.sdpMlineIndexes = wrappedItems } - @objc public func setSdpMlineIndex(_ valueParam: UInt32) { - proto.sdpMlineIndex = valueParam + @objc public func addSdpMids(_ valueParam: String) { + var items = proto.sdpMids + items.append(valueParam) + proto.sdpMids = items } - @objc public func setSdpMid(_ valueParam: String) { - proto.sdpMid = valueParam + @objc public func setSdpMids(_ wrappedItems: [String]) { + proto.sdpMids = wrappedItems } @objc public func build() throws -> SNProtoCallMessage { @@ -737,31 +751,22 @@ extension SNProtoContent.SNProtoContentBuilder { @objc public let type: SNProtoCallMessageType - @objc public let sdp: String - - @objc public var sdpMlineIndex: UInt32 { - return proto.sdpMlineIndex - } - @objc public var hasSdpMlineIndex: Bool { - return proto.hasSdpMlineIndex + @objc public var sdps: [String] { + return proto.sdps } - @objc public var sdpMid: String? { - guard proto.hasSdpMid else { - return nil - } - return proto.sdpMid + @objc public var sdpMlineIndexes: [UInt32] { + return proto.sdpMlineIndexes } - @objc public var hasSdpMid: Bool { - return proto.hasSdpMid + + @objc public var sdpMids: [String] { + return proto.sdpMids } private init(proto: SessionProtos_CallMessage, - type: SNProtoCallMessageType, - sdp: String) { + type: SNProtoCallMessageType) { self.proto = proto self.type = type - self.sdp = sdp } @objc @@ -780,18 +785,12 @@ extension SNProtoContent.SNProtoContentBuilder { } let type = SNProtoCallMessageTypeWrap(proto.type) - guard proto.hasSdp else { - throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: sdp") - } - let sdp = proto.sdp - // MARK: - Begin Validation Logic for SNProtoCallMessage - // MARK: - End Validation Logic for SNProtoCallMessage - let result = SNProtoCallMessage(proto: proto, - type: type, - sdp: sdp) + type: type) return result } diff --git a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift index 350844f08..bf534a845 100644 --- a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift +++ b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift @@ -319,33 +319,11 @@ struct SessionProtos_CallMessage { /// Clears the value of `type`. Subsequent reads from it will return its default value. mutating func clearType() {self._type = nil} - /// @required - var sdp: String { - get {return _sdp ?? String()} - set {_sdp = newValue} - } - /// Returns true if `sdp` has been explicitly set. - var hasSdp: Bool {return self._sdp != nil} - /// Clears the value of `sdp`. Subsequent reads from it will return its default value. - mutating func clearSdp() {self._sdp = nil} + var sdps: [String] = [] - var sdpMlineIndex: UInt32 { - get {return _sdpMlineIndex ?? 0} - set {_sdpMlineIndex = newValue} - } - /// Returns true if `sdpMlineIndex` has been explicitly set. - var hasSdpMlineIndex: Bool {return self._sdpMlineIndex != nil} - /// Clears the value of `sdpMlineIndex`. Subsequent reads from it will return its default value. - mutating func clearSdpMlineIndex() {self._sdpMlineIndex = nil} + var sdpMlineIndexes: [UInt32] = [] - var sdpMid: String { - get {return _sdpMid ?? String()} - set {_sdpMid = newValue} - } - /// Returns true if `sdpMid` has been explicitly set. - var hasSdpMid: Bool {return self._sdpMid != nil} - /// Clears the value of `sdpMid`. Subsequent reads from it will return its default value. - mutating func clearSdpMid() {self._sdpMid = nil} + var sdpMids: [String] = [] var unknownFields = SwiftProtobuf.UnknownStorage() @@ -354,7 +332,7 @@ struct SessionProtos_CallMessage { case offer // = 1 case answer // = 2 case provisionalAnswer // = 3 - case iceCandidate // = 4 + case iceCandidates // = 4 init() { self = .offer @@ -365,7 +343,7 @@ struct SessionProtos_CallMessage { case 1: self = .offer case 2: self = .answer case 3: self = .provisionalAnswer - case 4: self = .iceCandidate + case 4: self = .iceCandidates default: return nil } } @@ -375,7 +353,7 @@ struct SessionProtos_CallMessage { case .offer: return 1 case .answer: return 2 case .provisionalAnswer: return 3 - case .iceCandidate: return 4 + case .iceCandidates: return 4 } } @@ -384,9 +362,6 @@ struct SessionProtos_CallMessage { init() {} fileprivate var _type: SessionProtos_CallMessage.TypeEnum? = nil - fileprivate var _sdp: String? = nil - fileprivate var _sdpMlineIndex: UInt32? = nil - fileprivate var _sdpMid: String? = nil } #if swift(>=4.2) @@ -1817,14 +1792,13 @@ extension SessionProtos_CallMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa static let protoMessageName: String = _protobuf_package + ".CallMessage" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "type"), - 2: .same(proto: "sdp"), - 3: .same(proto: "sdpMLineIndex"), - 4: .same(proto: "sdpMid"), + 2: .same(proto: "sdps"), + 3: .same(proto: "sdpMLineIndexes"), + 4: .same(proto: "sdpMids"), ] public var isInitialized: Bool { if self._type == nil {return false} - if self._sdp == nil {return false} return true } @@ -1835,9 +1809,9 @@ extension SessionProtos_CallMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { case 1: try { try decoder.decodeSingularEnumField(value: &self._type) }() - case 2: try { try decoder.decodeSingularStringField(value: &self._sdp) }() - case 3: try { try decoder.decodeSingularUInt32Field(value: &self._sdpMlineIndex) }() - case 4: try { try decoder.decodeSingularStringField(value: &self._sdpMid) }() + case 2: try { try decoder.decodeRepeatedStringField(value: &self.sdps) }() + case 3: try { try decoder.decodeRepeatedUInt32Field(value: &self.sdpMlineIndexes) }() + case 4: try { try decoder.decodeRepeatedStringField(value: &self.sdpMids) }() default: break } } @@ -1847,23 +1821,23 @@ extension SessionProtos_CallMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) } - if let v = self._sdp { - try visitor.visitSingularStringField(value: v, fieldNumber: 2) + if !self.sdps.isEmpty { + try visitor.visitRepeatedStringField(value: self.sdps, fieldNumber: 2) } - if let v = self._sdpMlineIndex { - try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3) + if !self.sdpMlineIndexes.isEmpty { + try visitor.visitRepeatedUInt32Field(value: self.sdpMlineIndexes, fieldNumber: 3) } - if let v = self._sdpMid { - try visitor.visitSingularStringField(value: v, fieldNumber: 4) + if !self.sdpMids.isEmpty { + try visitor.visitRepeatedStringField(value: self.sdpMids, fieldNumber: 4) } try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: SessionProtos_CallMessage, rhs: SessionProtos_CallMessage) -> Bool { if lhs._type != rhs._type {return false} - if lhs._sdp != rhs._sdp {return false} - if lhs._sdpMlineIndex != rhs._sdpMlineIndex {return false} - if lhs._sdpMid != rhs._sdpMid {return false} + if lhs.sdps != rhs.sdps {return false} + if lhs.sdpMlineIndexes != rhs.sdpMlineIndexes {return false} + if lhs.sdpMids != rhs.sdpMids {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -1874,7 +1848,7 @@ extension SessionProtos_CallMessage.TypeEnum: SwiftProtobuf._ProtoNameProviding 1: .same(proto: "OFFER"), 2: .same(proto: "ANSWER"), 3: .same(proto: "PROVISIONAL_ANSWER"), - 4: .same(proto: "ICE_CANDIDATE"), + 4: .same(proto: "ICE_CANDIDATES"), ] } diff --git a/SessionMessagingKit/Protos/SessionProtos.proto b/SessionMessagingKit/Protos/SessionProtos.proto index 7cd083135..a2927a698 100644 --- a/SessionMessagingKit/Protos/SessionProtos.proto +++ b/SessionMessagingKit/Protos/SessionProtos.proto @@ -57,15 +57,16 @@ message CallMessage { OFFER = 1; ANSWER = 2; PROVISIONAL_ANSWER = 3; - ICE_CANDIDATE = 4; + ICE_CANDIDATES = 4; } + // Multiple ICE candidates may be batched together for performance + // @required - required Type type = 1; - // @required - required string sdp = 2; - optional uint32 sdpMLineIndex = 3; - optional string sdpMid = 4; + required Type type = 1; + repeated string sdps = 2; + repeated uint32 sdpMLineIndexes = 3; + repeated string sdpMids = 4; } message KeyPair { diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 9ae682d0c..9e0919e43 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -269,12 +269,20 @@ extension MessageReceiver { handleOfferCallMessage?(message) case .answer: print("[Calls] Received answer message.") - let sdp = RTCSessionDescription(type: .answer, sdp: message.sdp!) + let sdp = RTCSessionDescription(type: .answer, sdp: message.sdps![0]) webRTCWrapper.handleRemoteSDP(sdp, from: message.sender!) case .provisionalAnswer: break // TODO: Implement - case let .iceCandidate(sdpMLineIndex, sdpMid): - let candidate = RTCIceCandidate(sdp: message.sdp!, sdpMLineIndex: Int32(sdpMLineIndex), sdpMid: sdpMid) - webRTCWrapper.handleICECandidate(candidate) + case let .iceCandidates(sdpMLineIndexes, sdpMids): + var candidates: [RTCIceCandidate] = [] + let sdps = message.sdps! + for i in 0.. Promise { + public static func execute(_ verb: Verb, _ url: String, body: Data?, timeout: TimeInterval = HTTP.timeout, useSeedNodeURLSession: Bool = false) -> Promise { var request = URLRequest(url: URL(string: url)!) request.httpMethod = verb.rawValue request.httpBody = body @@ -117,9 +117,6 @@ public enum HTTP { request.allHTTPHeaderFields?.removeValue(forKey: "User-Agent") request.setValue("WhatsApp", forHTTPHeaderField: "User-Agent") // Set a fake value request.setValue("en-us", forHTTPHeaderField: "Accept-Language") // Set a fake value - headers.forEach { (key, value) in - request.setValue(value, forHTTPHeaderField: key) - } let (promise, seal) = Promise.pending() let urlSession = useSeedNodeURLSession ? seedNodeURLSession : snodeURLSession let task = urlSession.dataTask(with: request) { data, response, error in