From b8bee2bd701448b84b15a49eb963735d2495aad8 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Mon, 11 May 2020 18:25:17 +0400 Subject: [PATCH] Use internal signalling --- Telegram/NotificationService/Serialization.m | 2 +- submodules/TelegramApi/Sources/Api0.swift | 3 +- submodules/TelegramApi/Sources/Api1.swift | 24 + submodules/TelegramApi/Sources/Api3.swift | 15 + .../Sources/AccountIntermediateState.swift | 17 +- .../Sources/AccountStateManagementUtils.swift | 9 +- .../Sources/AccountStateManager.swift | 5 + .../Sources/CallSessionManager.swift | 74 ++- .../TelegramCore/Sources/Serialization.swift | 2 +- .../Sources/OngoingCallContext.swift | 15 +- .../TgVoip/OngoingCallThreadLocalContext.h | 5 +- .../Sources/OngoingCallThreadLocalContext.mm | 447 ++++++------------ 12 files changed, 293 insertions(+), 325 deletions(-) diff --git a/Telegram/NotificationService/Serialization.m b/Telegram/NotificationService/Serialization.m index 04d88804e7..f886eaeea4 100644 --- a/Telegram/NotificationService/Serialization.m +++ b/Telegram/NotificationService/Serialization.m @@ -3,7 +3,7 @@ @implementation Serialization - (NSUInteger)currentLayer { - return 113; + return 114; } - (id _Nullable)parseMessage:(NSData * _Nullable)data { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 9279ce5c35..bc0471dc1c 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -250,6 +250,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[654302845] = { return Api.Update.parse_updateDialogFilter($0) } dict[-1512627963] = { return Api.Update.parse_updateDialogFilterOrder($0) } dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) } + dict[643940105] = { return Api.Update.parse_updatePhoneCallSignalingData($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } @@ -841,7 +842,7 @@ public struct Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") return nil } } diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 1312a12efc..b1eef5d412 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -5894,6 +5894,7 @@ public extension Api { case updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) case updateDialogFilterOrder(order: [Int32]) case updateDialogFilters + case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -6566,6 +6567,13 @@ public extension Api { buffer.appendInt32(889491791) } + break + case .updatePhoneCallSignalingData(let phoneCallId, let data): + if boxed { + buffer.appendInt32(643940105) + } + serializeInt64(phoneCallId, buffer: buffer, boxed: false) + serializeBytes(data, buffer: buffer, boxed: false) break } } @@ -6732,6 +6740,8 @@ public extension Api { return ("updateDialogFilterOrder", [("order", order)]) case .updateDialogFilters: return ("updateDialogFilters", []) + case .updatePhoneCallSignalingData(let phoneCallId, let data): + return ("updatePhoneCallSignalingData", [("phoneCallId", phoneCallId), ("data", data)]) } } @@ -8063,6 +8073,20 @@ public extension Api { public static func parse_updateDialogFilters(_ reader: BufferReader) -> Update? { return Api.Update.updateDialogFilters } + public static func parse_updatePhoneCallSignalingData(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Buffer? + _2 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updatePhoneCallSignalingData(phoneCallId: _1!, data: _2!) + } + else { + return nil + } + } } public enum PopularContact: TypeConstructorDescription { diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 9fb5d33fff..84235c1d17 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -6477,6 +6477,21 @@ public extension Api { return result }) } + + public static func sendSignalingData(peer: Api.InputPhoneCall, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-8744061) + peer.serialize(buffer, true) + serializeBytes(data, buffer: buffer, boxed: false) + return (FunctionDescription(name: "phone.sendSignalingData", parameters: [("peer", peer), ("data", data)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } } } } diff --git a/submodules/TelegramCore/Sources/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/AccountIntermediateState.swift index bf19ff8002..e55221d479 100644 --- a/submodules/TelegramCore/Sources/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/AccountIntermediateState.swift @@ -98,6 +98,7 @@ enum AccountStateMutationOperation { case UpdateRecentGifs case UpdateChatInputState(PeerId, SynchronizeableChatInputState?) case UpdateCall(Api.PhoneCall) + case AddCallSignalingData(Int64, Data) case UpdateLangPack(String, Api.LangPackDifference?) case UpdateMinAvailableMessage(MessageId) case UpdatePeerChatInclusion(peerId: PeerId, groupId: PeerGroupId, changedGroup: Bool) @@ -425,6 +426,10 @@ struct AccountMutableState { self.addOperation(.UpdateCall(call)) } + mutating func addCallSignalingData(callId: Int64, data: Data) { + self.addOperation(.AddCallSignalingData(callId, data)) + } + mutating func addSyncChatListFilters() { self.addOperation(.SyncChatListFilters) } @@ -439,7 +444,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter: break case let .AddMessages(messages, location): for message in messages { @@ -555,6 +560,7 @@ struct AccountReplayedFinalState { let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedCalls: [Api.PhoneCall] + let addedCallSignalingData: [(Int64, Data)] let updatedPeersNearby: [PeerNearby]? let isContactUpdates: [(PeerId, Bool)] let delayNotificatonsUntil: Int32? @@ -566,6 +572,7 @@ struct AccountFinalStateEvents { let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedCalls: [Api.PhoneCall] + let addedCallSignalingData: [(Int64, Data)] let updatedPeersNearby: [PeerNearby]? let isContactUpdates: [(PeerId, Bool)] let displayAlerts: [(text: String, isDropAuth: Bool)] @@ -576,15 +583,16 @@ struct AccountFinalStateEvents { let authorizationListUpdated: Bool var isEmpty: Bool { - return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated + return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated } - init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false) { + init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false) { self.addedIncomingMessageIds = addedIncomingMessageIds self.wasScheduledMessageIds = wasScheduledMessageIds self.updatedTypingActivities = updatedTypingActivities self.updatedWebpages = updatedWebpages self.updatedCalls = updatedCalls + self.addedCallSignalingData = addedCallSignalingData self.updatedPeersNearby = updatedPeersNearby self.isContactUpdates = isContactUpdates self.displayAlerts = displayAlerts @@ -601,6 +609,7 @@ struct AccountFinalStateEvents { self.updatedTypingActivities = state.updatedTypingActivities self.updatedWebpages = state.updatedWebpages self.updatedCalls = state.updatedCalls + self.addedCallSignalingData = state.addedCallSignalingData self.updatedPeersNearby = state.updatedPeersNearby self.isContactUpdates = state.isContactUpdates self.displayAlerts = state.state.state.displayAlerts @@ -634,6 +643,6 @@ struct AccountFinalStateEvents { let externallyUpdatedPeerId = self.externallyUpdatedPeerId.union(other.externallyUpdatedPeerId) let authorizationListUpdated = self.authorizationListUpdated || other.authorizationListUpdated - return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated) + return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated) } } diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 2d51dc0602..0d08a5a43c 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -1282,6 +1282,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo updatedState.addUpdateChatInputState(peerId: peer.peerId, state: inputState) case let .updatePhoneCall(phoneCall): updatedState.addUpdateCall(phoneCall) + case let .updatePhoneCallSignalingData(phoneCallId, data): + updatedState.addCallSignalingData(callId: phoneCallId, data: data.makeData()) case let .updateLangPackTooLong(langCode): updatedState.updateLangPack(langCode: langCode, difference: nil) case let .updateLangPack(difference): @@ -2073,7 +2075,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddScheduledMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -2157,6 +2159,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP var updatedSecretChatTypingActivities = Set() var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:] var updatedCalls: [Api.PhoneCall] = [] + var addedCallSignalingData: [(Int64, Data)] = [] var updatedPeersNearby: [PeerNearby]? var isContactUpdates: [(PeerId, Bool)] = [] var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = [] @@ -2760,6 +2763,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP }) case let .UpdateCall(call): updatedCalls.append(call) + case let .AddCallSignalingData(callId, data): + addedCallSignalingData.append((callId, data)) case let .UpdateLangPack(langCode, difference): if let difference = difference { if langPackDifferences[langCode] == nil { @@ -3141,5 +3146,5 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP requestChatListFiltersSync(transaction: transaction) } - return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil) + return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil) } diff --git a/submodules/TelegramCore/Sources/AccountStateManager.swift b/submodules/TelegramCore/Sources/AccountStateManager.swift index 88236272f1..99d3bb4b47 100644 --- a/submodules/TelegramCore/Sources/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/AccountStateManager.swift @@ -653,6 +653,11 @@ public final class AccountStateManager { strongSelf.callSessionManager.updateSession(call, completion: { _ in }) } } + if !events.addedCallSignalingData.isEmpty { + for (id, data) in events.addedCallSignalingData { + strongSelf.callSessionManager.addCallSignalingData(id: id, data: data) + } + } if !events.isContactUpdates.isEmpty { strongSelf.addIsContactUpdates(events.isContactUpdates) } diff --git a/submodules/TelegramCore/Sources/CallSessionManager.swift b/submodules/TelegramCore/Sources/CallSessionManager.swift index b7d7ca2910..cae4f135de 100644 --- a/submodules/TelegramCore/Sources/CallSessionManager.swift +++ b/submodules/TelegramCore/Sources/CallSessionManager.swift @@ -213,12 +213,15 @@ private final class CallSessionContext { let isOutgoing: Bool var state: CallSessionInternalState let subscribers = Bag<(CallSession) -> Void>() + let signalingSubscribers = Bag<(Data) -> Void>() + + let signalingDisposables = DisposableSet() let acknowledgeIncomingCallDisposable = MetaDisposable() var isEmpty: Bool { if case .terminated = self.state { - return self.subscribers.isEmpty + return self.subscribers.isEmpty && self.signalingSubscribers.isEmpty } else { return false } @@ -321,6 +324,31 @@ private final class CallSessionManagerContext { } } + func callSignalingData(internalId: CallSessionInternalId) -> Signal { + let queue = self.queue + return Signal { [weak self] subscriber in + let disposable = MetaDisposable() + queue.async { + if let strongSelf = self, let context = strongSelf.contexts[internalId] { + let index = context.signalingSubscribers.add { next in + subscriber.putNext(next) + } + disposable.set(ActionDisposable { + queue.async { + if let strongSelf = self, let context = strongSelf.contexts[internalId] { + context.signalingSubscribers.remove(index) + if context.isEmpty { + strongSelf.contexts.removeValue(forKey: internalId) + } + } + } + }) + } + } + return disposable + } + } + private func ringingStatesValue() -> [CallSessionRingingState] { var ringingContexts: [CallSessionRingingState] = [] for (id, context) in self.contexts { @@ -523,6 +551,17 @@ private final class CallSessionManagerContext { } } + func sendSignalingData(internalId: CallSessionInternalId, data: Data) { + if let context = self.contexts[internalId] { + switch context.state { + case let .active(id, accessHash, _, _, _, _, _, _, _, _): + context.signalingDisposables.add(self.network.request(Api.functions.phone.sendSignalingData(peer: .inputPhoneCall(id: id, accessHash: accessHash), data: Buffer(data: data))).start()) + default: + break + } + } + } + func updateSession(_ call: Api.PhoneCall, completion: @escaping ((CallSessionRingingState, CallSession)?) -> Void) { var resultRingingState: (CallSessionRingingState, CallSession)? @@ -723,6 +762,15 @@ private final class CallSessionManagerContext { completion(resultRingingState) } + func addCallSignalingData(id: Int64, data: Data) { + guard let internalId = self.contextIdByStableId[id], let context = self.contexts[internalId] else { + return + } + for f in context.signalingSubscribers.copyItems() { + f(data) + } + } + private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? { var key = MTExp(self.network.encryptionProvider, gA, b, config.p.makeData())! @@ -818,6 +866,12 @@ public final class CallSessionManager { } } + func addCallSignalingData(id: Int64, data: Data) { + self.withContext { context in + context.addCallSignalingData(id: id, data: data) + } + } + public func drop(internalId: CallSessionInternalId, reason: DropCallReason, debugLog: Signal) { self.withContext { context in context.drop(internalId: internalId, reason: reason, debugLog: debugLog) @@ -857,6 +911,12 @@ public final class CallSessionManager { } } + public func sendSignalingData(internalId: CallSessionInternalId, data: Data) { + self.withContext { context in + context.sendSignalingData(internalId: internalId, data: data) + } + } + public func ringingStates() -> Signal<[CallSessionRingingState], NoError> { return Signal { [weak self] subscriber in let disposable = MetaDisposable() @@ -880,6 +940,18 @@ public final class CallSessionManager { return disposable } } + + public func callSignalingData(internalId: CallSessionInternalId) -> Signal { + return Signal { [weak self] subscriber in + let disposable = MetaDisposable() + self?.withContext { context in + disposable.set(context.callSignalingData(internalId: internalId).start(next: { next in + subscriber.putNext(next) + })) + } + return disposable + } + } } private enum AcceptedCall { diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift index bc4341298d..55bda07a9d 100644 --- a/submodules/TelegramCore/Sources/Serialization.swift +++ b/submodules/TelegramCore/Sources/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 113 + return 114 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 8d5289bf5c..7d50096de9 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -383,6 +383,8 @@ public final class OngoingCallContext { return self.contextState.get() } + private var signalingDataDisposable: Disposable? + private let receptionPromise = Promise(nil) public var reception: Signal { return self.receptionPromise.get() @@ -428,7 +430,9 @@ public final class OngoingCallContext { break } } - let context = OngoingCallThreadLocalContextWebrtcCustom(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtcCustom(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtcCustom(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtcCustom(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtcCustom), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath) + let context = OngoingCallThreadLocalContextWebrtcCustom(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtcCustom(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtcCustom(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtcCustom(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtcCustom), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in + callSessionManager?.sendSignalingData(internalId: internalId, data: data) + }) strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) context.stateChanged = { state in @@ -499,6 +503,15 @@ public final class OngoingCallContext { } } })) + + self.signalingDataDisposable = (callSessionManager.callSignalingData(internalId: internalId) + |> deliverOn(self.queue)).start(next: { [weak self] data in + self?.withContext { context in + if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { + context.receiveSignaling(data) + } + } + }) } deinit { diff --git a/submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h index eef74393c1..4f486fdc99 100644 --- a/submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h @@ -66,7 +66,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtcCustom) { @property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtcCustom); @property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t); -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtcCustom * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtcCustom)networkType dataSaving:(OngoingCallDataSavingWebrtcCustom)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtcCustom * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath; +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtcCustom * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtcCustom)networkType dataSaving:(OngoingCallDataSavingWebrtcCustom)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtcCustom * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData; - (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion; - (bool)needRate; @@ -75,6 +75,9 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtcCustom) { - (NSString * _Nullable)version; - (NSData * _Nonnull)getDerivedState; + +- (void)receiveSignalingData:(NSData * _Nonnull)data; + - (void)setIsMuted:(bool)isMuted; - (void)setNetworkType:(OngoingCallNetworkTypeWebrtcCustom)networkType; - (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion; diff --git a/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm index 7b3295ea6a..4336df0ff1 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm @@ -38,233 +38,6 @@ static void voipLog(NSString* format, ...) { } } -@class NativeWebSocketDelegate; - -API_AVAILABLE(ios(13.0)) -@interface NativeWebSocket : NSObject { - id _queue; - NativeWebSocketDelegate *_socketDelegate; - void (^_receivedData)(NSData *); - - NSURLSession *_session; - NSURLSessionWebSocketTask *_socket; -} - -@end - -API_AVAILABLE(ios(13.0)) -@interface NativeWebSocketDelegate: NSObject { - id _queue; - __weak NativeWebSocket *_target; -} - -@end - -@implementation NativeWebSocketDelegate - -- (instancetype)initWithQueue:(id)queue target:(NativeWebSocket *)target { - self = [super init]; - if (self != nil) { - _queue = queue; - _target = target; - } - return self; -} - -- (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketTask *)webSocketTask didOpenWithProtocol:(NSString *)protocol { -} - -- (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketTask *)webSocketTask didCloseWithCode:(NSURLSessionWebSocketCloseCode)closeCode reason:(NSData *)reason { -} - -@end - -@implementation NativeWebSocket - -- (instancetype)initWithQueue:(id)queue receivedData:(void (^)(NSData *))receivedData { - self = [super init]; - if (self != nil) { - _queue = queue; - _receivedData = [receivedData copy]; - _socketDelegate = [[NativeWebSocketDelegate alloc] initWithQueue:queue target:self]; - _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:_socketDelegate delegateQueue:nil]; - } - return self; -} - -- (void)connect { - _socket = [_session webSocketTaskWithURL:[[NSURL alloc] initWithString:@"ws://192.168.8.118:8080"]]; - [_socket resume]; - [self readMessage]; -} - -- (void)readMessage { - id queue = _queue; - __weak NativeWebSocket *weakSelf = self; - [_socket receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage * _Nullable message, NSError * _Nullable error) { - [queue dispatch:^{ - __strong NativeWebSocket *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - - if (error != nil) { - voipLog(@"WebSocket error: %@", error); - } else if (message.data != nil) { - if (strongSelf->_receivedData) { - strongSelf->_receivedData(message.data); - } - [strongSelf readMessage]; - } else { - [strongSelf readMessage]; - } - }]; - }]; -} - -- (void)sendData:(NSData *)data { - [_socket sendMessage:[[NSURLSessionWebSocketMessage alloc] initWithData:data] completionHandler:^(__unused NSError * _Nullable error) { - }]; -} - -- (void)disconned { - [_socket cancel]; -} - -@end - -@protocol NativeWebrtcSignallingClientDelegate - -@end - -API_AVAILABLE(ios(13.0)) -@interface NativeWebrtcSignallingClient : NSObject { - id _queue; - NativeWebSocket *_socket; - void (^_didReceiveSessionDescription)(RTCSessionDescription *); - void (^_didReceiveIceCandidate)(RTCIceCandidate *); -} - -@property (nonatomic, weak) id delegate; - -@end - -@implementation NativeWebrtcSignallingClient - -- (instancetype)initWithQueue:(id)queue didReceiveSessionDescription:(void (^)(RTCSessionDescription *))didReceiveSessionDescription didReceiveIceCandidate:(void (^)(RTCIceCandidate *))didReceiveIceCandidate { - self = [super init]; - if (self != nil) { - _queue = queue; - _didReceiveSessionDescription = [didReceiveSessionDescription copy]; - _didReceiveIceCandidate = [didReceiveIceCandidate copy]; - - __weak NativeWebrtcSignallingClient *weakSelf = self; - _socket = [[NativeWebSocket alloc] initWithQueue:queue receivedData:^(NSData *data) { - __strong NativeWebrtcSignallingClient *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - [strongSelf didReceiveData:data]; - }]; - } - return self; -} - -- (void)connect { - [_socket connect]; -} - -- (void)sendSdp:(RTCSessionDescription *)rtcSdp { - NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; - json[@"messageType"] = @"sessionDescription"; - json[@"sdp"] = rtcSdp.sdp; - if (rtcSdp.type == RTCSdpTypeOffer) { - json[@"type"] = @"offer"; - } else if (rtcSdp.type == RTCSdpTypePrAnswer) { - json[@"type"] = @"prAnswer"; - } else if (rtcSdp.type == RTCSdpTypeAnswer) { - json[@"type"] = @"answer"; - } - NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; - if (data != nil) { - [_socket sendData:data]; - } -} - -- (void)sendCandidate:(RTCIceCandidate *)rtcIceCandidate { - NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; - json[@"messageType"] = @"iceCandidate"; - json[@"sdp"] = rtcIceCandidate.sdp; - json[@"mLineIndex"] = @(rtcIceCandidate.sdpMLineIndex); - if (rtcIceCandidate.sdpMid != nil) { - json[@"sdpMid"] = rtcIceCandidate.sdpMid; - } - NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; - if (data != nil) { - [_socket sendData:data]; - } -} - -- (void)didReceiveData:(NSData *)data { - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; - if (![json isKindOfClass:[NSDictionary class]]) { - return; - } - NSString *messageType = json[@"messageType"]; - if (![messageType isKindOfClass:[NSString class]]) { - return; - } - - if ([messageType isEqualToString:@"sessionDescription"]) { - NSString *sdp = json[@"sdp"]; - if (![sdp isKindOfClass:[NSString class]]) { - return; - } - - NSString *typeString = json[@"type"]; - if (![typeString isKindOfClass:[NSString class]]) { - return; - } - - RTCSdpType type; - if ([typeString isEqualToString:@"offer"]) { - type = RTCSdpTypeOffer; - } else if ([typeString isEqualToString:@"prAnswer"]) { - type = RTCSdpTypePrAnswer; - } else if ([typeString isEqualToString:@"answer"]) { - type = RTCSdpTypeAnswer; - } else { - return; - } - - if (_didReceiveSessionDescription) { - _didReceiveSessionDescription([[RTCSessionDescription alloc] initWithType:type sdp:sdp]); - } - } else if ([messageType isEqualToString:@"iceCandidate"]) { - NSString *sdp = json[@"sdp"]; - if (![sdp isKindOfClass:[NSString class]]) { - return; - } - - NSNumber *mLineIndex = json[@"mLineIndex"]; - if (![mLineIndex isKindOfClass:[NSNumber class]]) { - return; - } - - NSString *sdpMidString = json[@"sdpMid"]; - NSString *sdpMid = nil; - if ([sdpMidString isKindOfClass:[NSString class]]) { - sdpMid = sdpMidString; - } - - if (_didReceiveIceCandidate) { - _didReceiveIceCandidate([[RTCIceCandidate alloc] initWithSdp:sdp sdpMLineIndex:[mLineIndex intValue] sdpMid:sdpMid]); - } - } -} - -@end - @interface NativePeerConnectionDelegate : NSObject { id _queue; void (^_didGenerateIceCandidate)(RTCIceCandidate *); @@ -361,6 +134,9 @@ API_AVAILABLE(ios(13.0)) id _queue; int32_t _contextId; + bool _isOutgoing; + void (^_sendSignalingData)(NSData * _Nonnull); + NativePeerConnectionDelegate *_peerConnectionDelegate; OngoingCallNetworkTypeWebrtcCustom _networkType; @@ -372,10 +148,10 @@ API_AVAILABLE(ios(13.0)) OngoingCallStateWebrtcCustom _state; int32_t _signalBars; - NativeWebrtcSignallingClient *_signallingClient; - RTCPeerConnectionFactory *_peerConnectionFactory; + RTCPeerConnection *_peerConnection; + RTCVideoCapturer *_videoCapturer; RTCVideoTrack *_localVideoTrack; RTCVideoTrack *_remoteVideoTrack; @@ -420,12 +196,15 @@ API_AVAILABLE(ios(13.0)) return 80; } -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtcCustom * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtcCustom)networkType dataSaving:(OngoingCallDataSavingWebrtcCustom)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtcCustom * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath { +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtcCustom * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtcCustom)networkType dataSaving:(OngoingCallDataSavingWebrtcCustom)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtcCustom * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData { self = [super init]; if (self != nil) { _queue = queue; assert([queue isCurrent]); + _isOutgoing = isOutgoing; + _sendSignalingData = [sendSignalingData copy]; + _callReceiveTimeout = 20.0; _callRingTimeout = 90.0; _callConnectTimeout = 30.0; @@ -468,7 +247,7 @@ API_AVAILABLE(ios(13.0)) if (strongSelf == nil) { return; } - [strongSelf->_signallingClient sendCandidate:iceCandidate]; + [strongSelf sendCandidate:iceCandidate]; } didChangeIceState: ^(OngoingCallStateWebrtcCustom state) { __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; if (strongSelf == nil) { @@ -501,58 +280,6 @@ API_AVAILABLE(ios(13.0)) _localVideoTrack = [_peerConnectionFactory videoTrackWithSource:videoSource trackId:@"video0"]; [_peerConnection addTrack:_localVideoTrack streamIds:@[streamId]]; - NSDictionary *mediaConstraints = @{ - kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueTrue, - kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueTrue - }; - - RTCMediaConstraints *connectionConstraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mediaConstraints optionalConstraints:nil]; - - _signallingClient = [[NativeWebrtcSignallingClient alloc] initWithQueue:queue didReceiveSessionDescription:^(RTCSessionDescription *sessionDescription) { - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - - if (strongSelf->_receivedRemoteDescription) { - return; - } - strongSelf->_receivedRemoteDescription = true; - - [strongSelf->_peerConnection setRemoteDescription:sessionDescription completionHandler:^(__unused NSError * _Nullable error) { - - }]; - - if (!isOutgoing) { - [strongSelf->_peerConnection answerForConstraints:connectionConstraints completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) { - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - - [strongSelf->_peerConnection setLocalDescription:sdp completionHandler:^(__unused NSError * _Nullable error) { - [queue dispatch:^{ - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - [strongSelf->_signallingClient sendSdp:sdp]; - }]; - }]; - }]; - } - } didReceiveIceCandidate:^(RTCIceCandidate *iceCandidate) { - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - - voipLog(@"didReceiveIceCandidate: %@", iceCandidate); - [strongSelf->_peerConnection addIceCandidate:iceCandidate]; - }]; - - [_signallingClient connect]; - if (isOutgoing) { id queue = _queue; NSDictionary *mediaConstraints = @{ @@ -596,7 +323,7 @@ API_AVAILABLE(ios(13.0)) return; } - [_signallingClient sendSdp:sessionDescription]; + [self sendSdp:sessionDescription]; __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; [_queue dispatchAfter:1.0 block:^{ __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; @@ -657,35 +384,6 @@ API_AVAILABLE(ios(13.0)) [cameraCapturer startCaptureWithDevice:frontCamera format:bestFormat fps:27 completionHandler:^(NSError * _Nonnull error) { }]; - - //add renderer - - /* - guard let capturer = self.videoCapturer as? RTCCameraVideoCapturer else { - return - } - - guard - let frontCamera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == .front }), - - // choose highest res - let format = (RTCCameraVideoCapturer.supportedFormats(for: frontCamera).sorted { (f1, f2) -> Bool in - let width1 = CMVideoFormatDescriptionGetDimensions(f1.formatDescription).width - let width2 = CMVideoFormatDescriptionGetDimensions(f2.formatDescription).width - return width1 < width2 - }).last, - - // choose highest fps - let fps = (format.videoSupportedFrameRateRanges.sorted { return $0.maxFrameRate < $1.maxFrameRate }.last) else { - return - } - - capturer.startCapture(with: frontCamera, - format: format, - fps: Int(fps.maxFrameRate)) - - self.localVideoTrack?.add(renderer) - */ } - (bool)needRate { @@ -716,6 +414,129 @@ API_AVAILABLE(ios(13.0)) return [NSData data]; } +- (void)sendSdp:(RTCSessionDescription *)rtcSdp { + NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; + json[@"messageType"] = @"sessionDescription"; + json[@"sdp"] = rtcSdp.sdp; + if (rtcSdp.type == RTCSdpTypeOffer) { + json[@"type"] = @"offer"; + } else if (rtcSdp.type == RTCSdpTypePrAnswer) { + json[@"type"] = @"prAnswer"; + } else if (rtcSdp.type == RTCSdpTypeAnswer) { + json[@"type"] = @"answer"; + } + NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; + if (data != nil) { + _sendSignalingData(data); + } +} + +- (void)sendCandidate:(RTCIceCandidate *)rtcIceCandidate { + NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; + json[@"messageType"] = @"iceCandidate"; + json[@"sdp"] = rtcIceCandidate.sdp; + json[@"mLineIndex"] = @(rtcIceCandidate.sdpMLineIndex); + if (rtcIceCandidate.sdpMid != nil) { + json[@"sdpMid"] = rtcIceCandidate.sdpMid; + } + NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; + if (data != nil) { + _sendSignalingData(data); + } +} + +- (void)receiveSignalingData:(NSData *)data { + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + if (![json isKindOfClass:[NSDictionary class]]) { + return; + } + NSString *messageType = json[@"messageType"]; + if (![messageType isKindOfClass:[NSString class]]) { + return; + } + + if ([messageType isEqualToString:@"sessionDescription"]) { + NSString *sdp = json[@"sdp"]; + if (![sdp isKindOfClass:[NSString class]]) { + return; + } + + NSString *typeString = json[@"type"]; + if (![typeString isKindOfClass:[NSString class]]) { + return; + } + + RTCSdpType type; + if ([typeString isEqualToString:@"offer"]) { + type = RTCSdpTypeOffer; + } else if ([typeString isEqualToString:@"prAnswer"]) { + type = RTCSdpTypePrAnswer; + } else if ([typeString isEqualToString:@"answer"]) { + type = RTCSdpTypeAnswer; + } else { + return; + } + + if (_receivedRemoteDescription) { + return; + } + _receivedRemoteDescription = true; + + RTCSessionDescription *sessionDescription = [[RTCSessionDescription alloc] initWithType:type sdp:sdp]; + + NSDictionary *mediaConstraints = @{ + kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueTrue, + kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueTrue + }; + + RTCMediaConstraints *connectionConstraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mediaConstraints optionalConstraints:nil]; + + [_peerConnection setRemoteDescription:sessionDescription completionHandler:^(__unused NSError * _Nullable error) { + }]; + + if (!_isOutgoing) { + __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; + [_peerConnection answerForConstraints:connectionConstraints completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) { + __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + + id queue = strongSelf->_queue; + [strongSelf->_peerConnection setLocalDescription:sdp completionHandler:^(__unused NSError * _Nullable error) { + [queue dispatch:^{ + __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + [strongSelf sendSdp:sdp]; + }]; + }]; + }]; + } + } else if ([messageType isEqualToString:@"iceCandidate"]) { + NSString *sdp = json[@"sdp"]; + if (![sdp isKindOfClass:[NSString class]]) { + return; + } + + NSNumber *mLineIndex = json[@"mLineIndex"]; + if (![mLineIndex isKindOfClass:[NSNumber class]]) { + return; + } + + NSString *sdpMidString = json[@"sdpMid"]; + NSString *sdpMid = nil; + if ([sdpMidString isKindOfClass:[NSString class]]) { + sdpMid = sdpMidString; + } + + RTCIceCandidate *iceCandidate = [[RTCIceCandidate alloc] initWithSdp:sdp sdpMLineIndex:[mLineIndex intValue] sdpMid:sdpMid]; + voipLog(@"didReceiveIceCandidate: %@", iceCandidate); + [_peerConnection addIceCandidate:iceCandidate]; + } +} + - (void)setIsMuted:(bool)isMuted { for (RTCRtpTransceiver *transceiver in _peerConnection.transceivers) { if ([transceiver isKindOfClass:[RTCAudioTrack class]]) {