From 763876773cedbe6740f68a717d50f9da9e74a7b0 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 5 May 2020 18:00:02 +0400 Subject: [PATCH] Build --- .gitmodules | 6 + Telegram/BUILD | 2 - submodules/AccountContext/BUILD | 1 - submodules/SettingsUI/BUILD | 1 - .../Sources/PresentationCall.swift | 6 +- .../Sources/PresentationCallManager.swift | 2 +- .../Sources/CallSessionManager.swift | 99 +++-- .../TelegramCore/Sources/RateCall.swift | 4 +- submodules/TelegramUI/BUILD | 3 - submodules/TelegramVoip/BUILD | 1 + .../Sources/OngoingCallContext.swift | 288 ++++++++++--- submodules/TgVoip/BUILD | 30 +- submodules/TgVoipWebrtc/BUILD | 56 +-- submodules/TgVoipWebrtc/Impl/Connector.cpp | 232 ++++++++++ submodules/TgVoipWebrtc/Impl/Connector.h | 79 ++++ submodules/TgVoipWebrtc/Impl/Controller.cpp | 225 ++++++++++ submodules/TgVoipWebrtc/Impl/Controller.h | 84 ++++ submodules/TgVoipWebrtc/Impl/Endpoint.cpp | 204 +++++++++ submodules/TgVoipWebrtc/Impl/Endpoint.h | 126 ++++++ submodules/TgVoipWebrtc/Impl/Layer92.cpp | 238 ++++++++++ submodules/TgVoipWebrtc/Impl/Layer92.h | 49 +++ submodules/TgVoipWebrtc/Impl/LayerBase.cpp | 17 + submodules/TgVoipWebrtc/Impl/LayerBase.h | 38 ++ .../TgVoipWebrtc/Impl/MediaEngineBase.h | 21 + .../TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp | 204 +++++++++ .../TgVoipWebrtc/Impl/MediaEngineWebrtc.h | 78 ++++ submodules/TgVoipWebrtc/Impl/Message.h | 134 ++++++ submodules/TgVoipWebrtc/Impl/Protocol10.cpp | 159 +++++++ submodules/TgVoipWebrtc/Impl/Protocol10.h | 44 ++ submodules/TgVoipWebrtc/Impl/ProtocolBase.cpp | 23 + submodules/TgVoipWebrtc/Impl/ProtocolBase.h | 33 ++ submodules/TgVoipWebrtc/Impl/TgVoip.cpp | 406 ++++++++++++++++++ submodules/TgVoipWebrtc/Impl/TgVoip.h | 179 ++++++++ .../TgVoip/OngoingCallThreadLocalContext.h | 22 +- .../Sources/OngoingCallThreadLocalContext.mm | 173 ++------ submodules/WalletUI/BUILD | 2 - submodules/openssl/BUILD | 2 + third-party/BUILD | 8 + third-party/depot_tools | 1 + third-party/webrtc/BUILD | 78 ++++ third-party/webrtc/build-webrtc-bazel.sh | 29 ++ third-party/webrtc/patch.sh | 71 +++ third-party/webrtc/webrtc-ios | 1 + 43 files changed, 3170 insertions(+), 289 deletions(-) create mode 100644 submodules/TgVoipWebrtc/Impl/Connector.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/Connector.h create mode 100644 submodules/TgVoipWebrtc/Impl/Controller.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/Controller.h create mode 100644 submodules/TgVoipWebrtc/Impl/Endpoint.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/Endpoint.h create mode 100644 submodules/TgVoipWebrtc/Impl/Layer92.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/Layer92.h create mode 100644 submodules/TgVoipWebrtc/Impl/LayerBase.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/LayerBase.h create mode 100644 submodules/TgVoipWebrtc/Impl/MediaEngineBase.h create mode 100644 submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h create mode 100644 submodules/TgVoipWebrtc/Impl/Message.h create mode 100644 submodules/TgVoipWebrtc/Impl/Protocol10.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/Protocol10.h create mode 100644 submodules/TgVoipWebrtc/Impl/ProtocolBase.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/ProtocolBase.h create mode 100644 submodules/TgVoipWebrtc/Impl/TgVoip.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/TgVoip.h create mode 160000 third-party/depot_tools create mode 100644 third-party/webrtc/BUILD create mode 100755 third-party/webrtc/build-webrtc-bazel.sh create mode 100644 third-party/webrtc/patch.sh create mode 160000 third-party/webrtc/webrtc-ios diff --git a/.gitmodules b/.gitmodules index d416d6ad33..408376ccba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,9 @@ [submodule "build-system/tulsi"] path = build-system/tulsi url = https://github.com/ali-fareed/tulsi.git +[submodule "third-party/depot_tools"] + path = third-party/depot_tools + url = https://chromium.googlesource.com/chromium/tools/depot_tools.git +[submodule "third-party/webrtc/webrtc-ios"] + path = third-party/webrtc/webrtc-ios +url=../webrtc-ios.git diff --git a/Telegram/BUILD b/Telegram/BUILD index f72f1025f2..61efb4a8e0 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -161,8 +161,6 @@ swift_library( "//submodules/PasswordSetupUI:PasswordSetupUIAssets", "//submodules/TelegramUI:TelegramUIResources", "//submodules/TelegramUI:TelegramUIAssets", - #"//submodules/WalletUI:WalletUIResources", - #"//submodules/WalletUI:WalletUIAssets", ], deps = [ "//submodules/TelegramUI:TelegramUI", diff --git a/submodules/AccountContext/BUILD b/submodules/AccountContext/BUILD index fd5976b7f7..f3f19db373 100644 --- a/submodules/AccountContext/BUILD +++ b/submodules/AccountContext/BUILD @@ -17,7 +17,6 @@ swift_library( "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/SyncCore:SyncCore", - "//submodules/WalletCore:WalletCore", ], visibility = [ "//visibility:public", diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index fa9dc7307a..51c58151f8 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -79,7 +79,6 @@ swift_library( "//submodules/InstantPageCache:InstantPageCache", "//submodules/AppBundle:AppBundle", "//submodules/ContextUI:ContextUI", - "//submodules/WalletUI:WalletUI", "//submodules/Markdown:Markdown", "//submodules/UndoUI:UndoUI", "//submodules/DeleteChatPeerActionSheetItem:DeleteChatPeerActionSheetItem", diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index 7ccec75fc1..104a1a4638 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -435,7 +435,7 @@ public final class PresentationCallImpl: PresentationCall { presentationState = .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)) case let .requesting(ringing): presentationState = .requesting(ringing) - case let .active(_, _, keyVisualHash, _, _, _): + case let .active(_, _, keyVisualHash, _, _, _, _): self.callWasActive = true if let callContextState = callContextState { switch callContextState { @@ -473,12 +473,12 @@ public final class PresentationCallImpl: PresentationCall { if let _ = audioSessionControl { self.audioSessionShouldBeActive.set(true) } - case let .active(id, key, _, connections, maxLayer, allowsP2P): + case let .active(id, key, _, connections, maxLayer, version, allowsP2P): self.audioSessionShouldBeActive.set(true) if let _ = audioSessionControl, !wasActive || previousControl == nil { let logName = "\(id.id)_\(id.accessHash)" - let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, connections: connections, maxLayer: maxLayer, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName) + let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName) self.ongoingContext = ongoingContext self.debugInfoValue.set(ongoingContext.debugInfo()) diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift index 2e5310dec2..9619863d31 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift @@ -76,7 +76,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { } public static var voipVersions: [String] { - return [OngoingCallContext.version] + return OngoingCallContext.versions } public init(accountManager: AccountManager, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) { diff --git a/submodules/TelegramCore/Sources/CallSessionManager.swift b/submodules/TelegramCore/Sources/CallSessionManager.swift index 3149687642..b7d7ca2910 100644 --- a/submodules/TelegramCore/Sources/CallSessionManager.swift +++ b/submodules/TelegramCore/Sources/CallSessionManager.swift @@ -90,13 +90,13 @@ public struct CallId: Equatable { } enum CallSessionInternalState { - case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data) + case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String]) case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, disposable: Disposable) case awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig) case requesting(a: Data, disposable: Disposable) case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?) case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable) - case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, allowsP2P: Bool) + case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool) case dropping(Disposable) case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool) } @@ -139,7 +139,7 @@ public enum CallSessionState { case ringing case accepting case requesting(ringing: Bool) - case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, allowsP2P: Bool) + case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool) case dropping case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions) @@ -155,8 +155,8 @@ public enum CallSessionState { self = .requesting(ringing: true) case let .requested(_, _, _, _, _, remoteConfirmationTimestamp): self = .requesting(ringing: remoteConfirmationTimestamp != nil) - case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, allowsP2P): - self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, allowsP2P: allowsP2P) + case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, allowsP2P): + self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, allowsP2P: allowsP2P) case .dropping: self = .dropping case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs): @@ -235,6 +235,15 @@ private final class CallSessionContext { } } +private func selectVersionOnAccept(localVersions: [String], remoteVersions: [String]) -> [String]? { + let filteredVersions = localVersions.filter(remoteVersions.contains) + if filteredVersions.isEmpty { + return nil + } else { + return [filteredVersions[0]] + } +} + private final class CallSessionManagerContext { private let queue: Queue private let postbox: Postbox @@ -254,7 +263,7 @@ private final class CallSessionManagerContext { self.postbox = postbox self.network = network self.maxLayer = maxLayer - self.versions = versions + self.versions = versions.reversed() self.addUpdates = addUpdates } @@ -338,7 +347,7 @@ private final class CallSessionManagerContext { } } - private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data) -> CallSessionInternalId? { + private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String]) -> CallSessionInternalId? { if self.contextIdByStableId[stableId] != nil { return nil } @@ -349,7 +358,7 @@ private final class CallSessionManagerContext { if randomStatus == 0 { let internalId = CallSessionInternalId() - let context = CallSessionContext(peerId: peerId, isOutgoing: false, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b)) + let context = CallSessionContext(peerId: peerId, isOutgoing: false, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions)) self.contexts[internalId] = context let queue = self.queue context.acknowledgeIncomingCallDisposable.set(self.network.request(Api.functions.phone.receivedCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash))).start(error: { [weak self] _ in @@ -374,7 +383,7 @@ private final class CallSessionManagerContext { var dropData: (CallSessionStableId, Int64, DropCallSessionReason)? var wasRinging = false switch context.state { - case let .ringing(id, accessHash, _, _): + case let .ringing(id, accessHash, _, _, _): wasRinging = true let internalReason: DropCallSessionReason switch reason { @@ -389,7 +398,7 @@ private final class CallSessionManagerContext { case let .accepting(id, accessHash, _, _, disposable): dropData = (id, accessHash, .abort) disposable.dispose() - case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _): + case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _): let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp) let internalReason: DropCallSessionReason switch reason { @@ -477,8 +486,13 @@ private final class CallSessionManagerContext { func accept(internalId: CallSessionInternalId) { if let context = self.contexts[internalId] { switch context.state { - case let .ringing(id, accessHash, gAHash, b): - context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: self.versions) |> deliverOn(self.queue)).start(next: { [weak self] result in + case let .ringing(id, accessHash, gAHash, b, remoteVersions): + guard var acceptVersions = selectVersionOnAccept(localVersions: self.versions, remoteVersions: remoteVersions) else { + self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) + return + } + acceptVersions = self.versions + context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in if let strongSelf = self, let context = strongSelf.contexts[internalId] { if case .accepting = context.state { switch result { @@ -489,9 +503,9 @@ private final class CallSessionManagerContext { case let .waiting(config): context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config) strongSelf.contextUpdated(internalId: internalId) - case let .call(config, gA, timestamp, connections, maxLayer, allowsP2P): + case let .call(config, gA, timestamp, connections, maxLayer, version, allowsP2P): if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) { - context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, allowsP2P: allowsP2P) + context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, allowsP2P: allowsP2P) strongSelf.contextUpdated(internalId: internalId) } else { strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) @@ -515,8 +529,18 @@ private final class CallSessionManagerContext { switch call { case .phoneCallEmpty: break - case let .phoneCallAccepted(flags, id, _, _, _, _, gB, _): + case let .phoneCallAccepted(flags, id, _, _, _, _, gB, remoteProtocol): + let remoteVersions: [String] + switch remoteProtocol { + case let .phoneCallProtocol(_, _, _, versions): + remoteVersions = versions + } if let internalId = self.contextIdByStableId[id] { + guard let selectedVersions = selectVersionOnAccept(localVersions: self.versions, remoteVersions: remoteVersions) else { + self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) + return + } + if let context = self.contexts[internalId] { switch context.state { case let .requested(_, accessHash, a, gA, config, _): @@ -543,7 +567,7 @@ private final class CallSessionManagerContext { let keyVisualHash = MTSha256(key + gA)! - context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: self.versions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in + context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state { if let updatedCall = updatedCall { strongSelf.updateSession(updatedCall, completion: { _ in }) @@ -586,7 +610,7 @@ private final class CallSessionManagerContext { disposable.dispose() context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) self.contextUpdated(internalId: internalId) - case let .active(id, accessHash, _, _, _, _, _, _, _): + case let .active(id, accessHash, _, _, _, _, _, _, _, _): context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) self.contextUpdated(internalId: internalId) case let .awaitingConfirmation(id, accessHash, _, _, _): @@ -603,7 +627,7 @@ private final class CallSessionManagerContext { disposable.dispose() context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false) self.contextUpdated(internalId: internalId) - case let .ringing(id, accessHash, _, _): + case let .ringing(id, accessHash, _, _, _): context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) self.ringingStatesUpdated() self.contextUpdated(internalId: internalId) @@ -625,9 +649,13 @@ private final class CallSessionManagerContext { if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) { if keyFingerprint == calculatedKeyId { switch callProtocol { - case let .phoneCallProtocol(_, _, maxLayer, _): - context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, allowsP2P: allowsP2P) - self.contextUpdated(internalId: internalId) + case let .phoneCallProtocol(_, _, maxLayer, versions): + if !versions.isEmpty { + context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: allowsP2P) + self.contextUpdated(internalId: internalId) + } else { + self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) + } } } else { self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) @@ -637,18 +665,27 @@ private final class CallSessionManagerContext { } case let .confirming(id, accessHash, key, keyId, keyVisualHash, _): switch callProtocol { - case let .phoneCallProtocol(_, _, maxLayer, _): - context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, allowsP2P: allowsP2P) - self.contextUpdated(internalId: internalId) + case let .phoneCallProtocol(_, _, maxLayer, versions): + if !versions.isEmpty { + context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: allowsP2P) + self.contextUpdated(internalId: internalId) + } else { + self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) + } } } } else { assertionFailure() } } - case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, _): + case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol): + let versions: [String] + switch requestedProtocol { + case let .phoneCallProtocol(_, _, _, libraryVersions): + versions = libraryVersions + } if self.contextIdByStableId[id] == nil { - let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData()) + let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions) if let internalId = internalId { var resultRingingStateValue: CallSessionRingingState? for ringingState in self.ringingStatesValue() { @@ -847,7 +884,7 @@ public final class CallSessionManager { private enum AcceptedCall { case waiting(config: SecretChatEncryptionConfig) - case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, allowsP2P: Bool) + case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool) } private enum AcceptCallResult { @@ -896,8 +933,12 @@ private func acceptCallSession(postbox: Postbox, network: Network, stableId: Cal case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate): if id == stableId { switch callProtocol{ - case let .phoneCallProtocol(_, _, maxLayer, _): - return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, allowsP2P: (flags & (1 << 5)) != 0)) + case let .phoneCallProtocol(_, _, maxLayer, versions): + if !versions.isEmpty { + return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: (flags & (1 << 5)) != 0)) + } else { + return .failed + } } } else { return .failed diff --git a/submodules/TelegramCore/Sources/RateCall.swift b/submodules/TelegramCore/Sources/RateCall.swift index dbc28f7cf7..96967d9271 100644 --- a/submodules/TelegramCore/Sources/RateCall.swift +++ b/submodules/TelegramCore/Sources/RateCall.swift @@ -16,6 +16,8 @@ public func rateCall(account: Account, callId: CallId, starsCount: Int32, commen public func saveCallDebugLog(network: Network, callId: CallId, log: String) -> Signal { return network.request(Api.functions.phone.saveCallDebug(peer: Api.InputPhoneCall.inputPhoneCall(id: callId.id, accessHash: callId.accessHash), debug: .dataJSON(data: log))) - |> retryRequest + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } |> map { _ in } } diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 585f4d3d3e..5bfe36872d 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -33,7 +33,6 @@ swift_library( "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/AccountContext:AccountContext", "//submodules/LegacyComponents:LegacyComponents", - "//submodules/TgVoip:TgVoip", "//submodules/lottie-ios:Lottie", "//submodules/FFMpegBinding:FFMpegBinding", "//submodules/WebPBinding:WebPBinding", @@ -185,8 +184,6 @@ swift_library( "//submodules/MessageReactionListUI:MessageReactionListUI", "//submodules/SegmentedControlNode:SegmentedControlNode", "//submodules/AppBundle:AppBundle", - "//submodules/WalletUI:WalletUI", - "//submodules/WalletCore:WalletCore", "//submodules/Markdown:Markdown", "//submodules/SearchPeerMembers:SearchPeerMembers", "//submodules/WidgetItems:WidgetItems", diff --git a/submodules/TelegramVoip/BUILD b/submodules/TelegramVoip/BUILD index 0fa6b09a6d..028b9e5ea2 100644 --- a/submodules/TelegramVoip/BUILD +++ b/submodules/TelegramVoip/BUILD @@ -13,6 +13,7 @@ swift_library( "//submodules/Postbox:Postbox", "//submodules/TelegramUIPreferences:TelegramUIPreferences", "//submodules/TgVoip:TgVoip", + "//submodules/TgVoipWebrtc:TgVoipWebrtc", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 825d18935c..243429522f 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -4,12 +4,18 @@ import TelegramCore import SyncCore import Postbox import TelegramUIPreferences + import TgVoip +import TgVoipWebrtc private func callConnectionDescription(_ connection: CallSessionConnection) -> OngoingCallConnectionDescription { return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) } +private func callConnectionDescriptionWebrtc(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtc { + return OngoingCallConnectionDescriptionWebrtc(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) +} + private let callLogsLimit = 20 public func callLogNameForId(id: Int64, account: Account) -> String? { @@ -68,6 +74,11 @@ private let setupLogs: Bool = { Logger.shared.log("TGVOIP", value) } }) + OngoingCallThreadLocalContextWebrtc.setupLoggingFunction({ value in + if let value = value { + Logger.shared.log("TGVOIP", value) + } + }) return true }() @@ -98,6 +109,26 @@ private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCal } } +private final class OngoingCallThreadLocalContextQueueWebrtcImpl: NSObject, OngoingCallThreadLocalContextQueueWebrtc { + private let queue: Queue + + init(queue: Queue) { + self.queue = queue + + super.init() + } + + func dispatch(_ f: @escaping () -> Void) { + self.queue.async { + f() + } + } + + func isCurrent() -> Bool { + return self.queue.isCurrent() + } +} + private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetworkType { switch type { case .none: @@ -118,6 +149,26 @@ private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetwor } } +private func ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtc { + switch type { + case .none: + return .wifi + case .wifi: + return .wifi + case let .cellular(cellular): + switch cellular { + case .edge: + return .cellularEdge + case .gprs: + return .cellularGprs + case .thirdG, .unknown: + return .cellular3g + case .lte: + return .cellularLte + } + } +} + private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCallDataSaving { switch type { case .never: @@ -131,6 +182,122 @@ private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCal } } +private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtc { + switch type { + case .never: + return .never + case .cellular: + return .cellular + case .always: + return .always + default: + return .never + } +} + +private protocol OngoingCallThreadLocalContextProtocol: class { + func nativeSetNetworkType(_ type: NetworkType) + func nativeSetIsMuted(_ value: Bool) + func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void) + func nativeDebugInfo() -> String + func nativeVersion() -> String + func nativeGetDerivedState() -> Data +} + +private final class OngoingCallThreadLocalContextHolder { + let context: OngoingCallThreadLocalContextProtocol + + init(_ context: OngoingCallThreadLocalContextProtocol) { + self.context = context + } +} + +extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol { + func nativeSetNetworkType(_ type: NetworkType) { + self.setNetworkType(ongoingNetworkTypeForType(type)) + } + + func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void) { + self.stop(completion) + } + + func nativeSetIsMuted(_ value: Bool) { + self.setIsMuted(value) + } + + func nativeDebugInfo() -> String { + return self.debugInfo() ?? "" + } + + func nativeVersion() -> String { + return self.version() ?? "" + } + + func nativeGetDerivedState() -> Data { + return self.getDerivedState() + } +} + +extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProtocol { + func nativeSetNetworkType(_ type: NetworkType) { + self.setNetworkType(ongoingNetworkTypeForTypeWebrtc(type)) + } + + func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void) { + self.stop(completion) + } + + func nativeSetIsMuted(_ value: Bool) { + self.setIsMuted(value) + } + + func nativeDebugInfo() -> String { + return self.debugInfo() ?? "" + } + + func nativeVersion() -> String { + return self.version() ?? "" + } + + func nativeGetDerivedState() -> Data { + return self.getDerivedState() + } +} + +private extension OngoingCallContextState { + init(_ state: OngoingCallState) { + switch state { + case .initializing: + self = .initializing + case .connected: + self = .connected + case .failed: + self = .failed + case .reconnecting: + self = .reconnecting + default: + self = .failed + } + } +} + +private extension OngoingCallContextState { + init(_ state: OngoingCallStateWebrtc) { + switch state { + case .initializing: + self = .initializing + case .connected: + self = .connected + case .failed: + self = .failed + case .reconnecting: + self = .reconnecting + default: + self = .failed + } + } +} + public final class OngoingCallContext { public let internalId: CallSessionInternalId @@ -138,27 +305,11 @@ public final class OngoingCallContext { private let account: Account private let callSessionManager: CallSessionManager - private var contextRef: Unmanaged? + private var contextRef: Unmanaged? - private let contextState = Promise(nil) + private let contextState = Promise(nil) public var state: Signal { return self.contextState.get() - |> map { - $0.flatMap { - switch $0 { - case .initializing: - return .initializing - case .connected: - return .connected - case .failed: - return .failed - case .reconnecting: - return .reconnecting - default: - return .failed - } - } - } } private let receptionPromise = Promise(nil) @@ -170,16 +321,17 @@ public final class OngoingCallContext { private var networkTypeDisposable: Disposable? public static var maxLayer: Int32 { - return OngoingCallThreadLocalContext.maxLayer() + return max(OngoingCallThreadLocalContext.maxLayer(), OngoingCallThreadLocalContextWebrtc.maxLayer()) } - public static var version: String { - return OngoingCallThreadLocalContext.version() + public static var versions: [String] { + return [OngoingCallThreadLocalContext.version(), OngoingCallThreadLocalContextWebrtc.version()] } - public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, connections: CallSessionConnectionSet, maxLayer: Int32, allowP2P: Bool, audioSessionActive: Signal, logName: String) { + public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, audioSessionActive: Signal, logName: String) { let _ = setupLogs OngoingCallThreadLocalContext.applyServerConfig(serializedData) + OngoingCallThreadLocalContextWebrtc.applyServerConfig(serializedData) self.internalId = internalId self.account = account @@ -195,31 +347,59 @@ public final class OngoingCallContext { |> take(1) |> deliverOn(queue)).start(next: { [weak self] _ in if let strongSelf = self { - var voipProxyServer: VoipProxyServer? - if let proxyServer = proxyServer { - switch proxyServer.connection { - case let .socks5(username, password): - voipProxyServer = VoipProxyServer(host: proxyServer.host, port: proxyServer.port, username: username, password: password) - case .mtp: - break + if version == OngoingCallThreadLocalContextWebrtc.version() { + var voipProxyServer: VoipProxyServerWebrtc? + if let proxyServer = proxyServer { + switch proxyServer.connection { + case let .socks5(username, password): + voipProxyServer = VoipProxyServerWebrtc(host: proxyServer.host, port: proxyServer.port, username: username, password: password) + case .mtp: + break + } } - } - let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath) - - strongSelf.contextRef = Unmanaged.passRetained(context) - context.stateChanged = { state in - self?.contextState.set(.single(state)) - } - context.signalBarsChanged = { signalBars in - self?.receptionPromise.set(.single(signalBars)) - } - - strongSelf.networkTypeDisposable = (updatedNetworkType - |> deliverOn(queue)).start(next: { networkType in - self?.withContext { context in - context.setNetworkType(ongoingNetworkTypeForType(networkType)) + let context = OngoingCallThreadLocalContextWebrtc(queue: OngoingCallThreadLocalContextQueueWebrtcImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtc(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtc), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath) + + strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) + context.stateChanged = { state in + self?.contextState.set(.single(OngoingCallContextState(state))) } - }) + context.signalBarsChanged = { signalBars in + self?.receptionPromise.set(.single(signalBars)) + } + + strongSelf.networkTypeDisposable = (updatedNetworkType + |> deliverOn(queue)).start(next: { networkType in + self?.withContext { context in + context.nativeSetNetworkType(networkType) + } + }) + } else { + var voipProxyServer: VoipProxyServer? + if let proxyServer = proxyServer { + switch proxyServer.connection { + case let .socks5(username, password): + voipProxyServer = VoipProxyServer(host: proxyServer.host, port: proxyServer.port, username: username, password: password) + case .mtp: + break + } + } + let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath) + + strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) + context.stateChanged = { state in + self?.contextState.set(.single(OngoingCallContextState(state))) + } + context.signalBarsChanged = { signalBars in + self?.receptionPromise.set(.single(signalBars)) + } + + strongSelf.networkTypeDisposable = (updatedNetworkType + |> deliverOn(queue)).start(next: { networkType in + self?.withContext { context in + context.nativeSetNetworkType(networkType) + } + }) + } } })) } @@ -234,18 +414,18 @@ public final class OngoingCallContext { self.networkTypeDisposable?.dispose() } - private func withContext(_ f: @escaping (OngoingCallThreadLocalContext) -> Void) { + private func withContext(_ f: @escaping (OngoingCallThreadLocalContextProtocol) -> Void) { self.queue.async { if let contextRef = self.contextRef { let context = contextRef.takeUnretainedValue() - f(context) + f(context.context) } } } public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise) { self.withContext { context in - context.stop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in + context.nativeStop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in debugLogValue.set(.single(debugLog)) let delta = NetworkUsageStatsConnectionsEntry( cellular: NetworkUsageStatsDirectionsEntry( @@ -260,7 +440,7 @@ public final class OngoingCallContext { let _ = saveCallDebugLog(network: self.account.network, callId: callId, log: debugLog).start() } } - let derivedState = context.getDerivedState() + let derivedState = context.nativeGetDerivedState() let _ = updateVoipDerivedStateInteractively(postbox: self.account.postbox, { _ in return VoipDerivedState(data: derivedState) }).start() @@ -269,18 +449,16 @@ public final class OngoingCallContext { public func setIsMuted(_ value: Bool) { self.withContext { context in - context.setIsMuted(value) + context.nativeSetIsMuted(value) } } public func debugInfo() -> Signal<(String, String), NoError> { let poll = Signal<(String, String), NoError> { subscriber in self.withContext { context in - let version = context.version() - let debugInfo = context.debugInfo() - if let version = version, let debugInfo = debugInfo { - subscriber.putNext((version, debugInfo)) - } + let version = context.nativeVersion() + let debugInfo = context.nativeDebugInfo() + subscriber.putNext((version, debugInfo)) subscriber.putCompletion() } diff --git a/submodules/TgVoip/BUILD b/submodules/TgVoip/BUILD index 2bd634904e..d316e32ecc 100644 --- a/submodules/TgVoip/BUILD +++ b/submodules/TgVoip/BUILD @@ -18,6 +18,31 @@ copts_x86 = [ "-DWEBRTC_IOS", ] +replace_symbols = [ + "WebRtcAgc_Process", + "rtc_FatalMessage", + "WebRtcAgc_UpdateAgcThresholds", + "WebRtcAgc_Init", + "WebRtcAgc_GetAddFarendError", + "WebRtcAgc_ZeroCtrl", + "WebRtcAgc_SaturationCtrl", + "WebRtcAgc_SpeakerInactiveCtrl", + "WebRtcAgc_ProcessAnalog", + "WebRtcAgc_set_config", + "WebRtcAgc_get_config", + "WebRtcAgc_ExpCurve", + "WebRtcAgc_Create", + "WebRtcAgc_Free", + "WebRtcAgc_AddFarend", + "WebRtcAgc_VirtualMic", + "WebRtcAgc_AddMic", + "WebRtcAgc_InitDigital", + "WebRtcAgc_AddFarendToDigital", + "WebRtcAgc_CalculateGainTable", + "WebRtcAgc_InitVad", + "WebRtcAgc_ProcessVad", +] + objc_library( name = "TgVoip", enable_modules = True, @@ -46,8 +71,11 @@ objc_library( "-I{}/PublicHeaders/TgVoip".format(package_name()), "-I{}/libtgvoip".format(package_name()), "-I{}/libtgvoip/webrtc_dsp".format(package_name()), + "-Isubmodules/Opus/Public/opus", "-DTGVOIP_USE_INSTALLED_OPUS", - ] + select({ + "-Drtc=rtc1", + "-Dwebrtc=webrtc1", + ] + ["-D{symbol}={symbol}1".format(symbol=symbol) for symbol in replace_symbols] + select({ "@build_bazel_rules_apple//apple:ios_armv7": copts_arm, "@build_bazel_rules_apple//apple:ios_arm64": copts_arm, "@build_bazel_rules_apple//apple:ios_x86_64": copts_x86, diff --git a/submodules/TgVoipWebrtc/BUILD b/submodules/TgVoipWebrtc/BUILD index 41f0219225..b7da3e0490 100644 --- a/submodules/TgVoipWebrtc/BUILD +++ b/submodules/TgVoipWebrtc/BUILD @@ -1,22 +1,8 @@ -copts_arm = [ - "-DTGVOIP_USE_CUSTOM_CRYPTO", - "-DWEBRTC_APM_DEBUG_DUMP=0", - "-DWEBRTC_POSIX", - "-DTGVOIP_HAVE_TGLOG", - "-DWEBRTC_NS_FLOAT", - "-DWEBRTC_IOS", - "-DWEBRTC_HAS_NEON", -] - -copts_x86 = [ - "-DTGVOIP_USE_CUSTOM_CRYPTO", - "-DWEBRTC_APM_DEBUG_DUMP=0", - "-DWEBRTC_POSIX", - "-DTGVOIP_HAVE_TGLOG", - "-DWEBRTC_NS_FLOAT", - "-DWEBRTC_IOS", -] +cc_library( + name = "webrtc_lib", + srcs = ["libwebrtc.a"], +) objc_library( name = "TgVoipWebrtc", @@ -26,38 +12,28 @@ objc_library( "Sources/**/*.m", "Sources/**/*.mm", "Sources/**/*.h", - "libtgvoip/*.m", - "libtgvoip/*.mm", - "libtgvoip/*.cpp", - "libtgvoip/audio/*.cpp", - "libtgvoip/video/*.cpp", - "libtgvoip/os/darwin/*.m", - "libtgvoip/os/darwin/*.mm", - "libtgvoip/os/darwin/*.cpp", - "libtgvoip/os/posix/*.cpp", - "libtgvoip/webrtc_dsp/**/*.c", - "libtgvoip/webrtc_dsp/**/*.cc", - "libtgvoip/webrtc_dsp/**/*.cpp", - ], exclude = ["libtgvoip/os/darwin/*OSX*"]), + "Impl/*.h", + "Impl/*.cpp", + ]), hdrs = glob([ "PublicHeaders/**/*.h", ]), copts = [ - "-I{}/PublicHeaders/TgVoip".format(package_name()), - "-I{}/libtgvoip".format(package_name()), - "-I{}/libtgvoip/webrtc_dsp".format(package_name()), - "-DTGVOIP_USE_INSTALLED_OPUS", - ] + select({ - "@build_bazel_rules_apple//apple:ios_armv7": copts_arm, - "@build_bazel_rules_apple//apple:ios_arm64": copts_arm, - "@build_bazel_rules_apple//apple:ios_x86_64": copts_x86, - }), + "-I{}/Impl".format(package_name()), + "-Ithird-party/webrtc/webrtc-ios/src".format(package_name()), + "-Ithird-party/webrtc/webrtc-ios/src/third_party/abseil-cpp".format(package_name()), + "-DWEBRTC_IOS", + "-DWEBRTC_MAC", + "-DWEBRTC_POSIX", + ], includes = [ "PublicHeaders", ], deps = [ + "//third-party/webrtc:webrtc_lib", "//submodules/MtProtoKit:MtProtoKit", "//submodules/Opus:opus", + "//submodules/openssl:openssl", ], sdk_frameworks = [ "Foundation", diff --git a/submodules/TgVoipWebrtc/Impl/Connector.cpp b/submodules/TgVoipWebrtc/Impl/Connector.cpp new file mode 100644 index 0000000000..15576f5c2f --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Connector.cpp @@ -0,0 +1,232 @@ +#include "Connector.h" + +#include "Endpoint.h" +#include "Layer92.h" +#include "MediaEngineWebrtc.h" +#include "Protocol10.h" + +#include "api/packet_socket_factory.h" +#include "rtc_base/task_utils/to_queued_task.h" + +#include + +const int64_t Connector::tcp_reconnect_delay = 5000; +const int64_t Connector::ping_interval_ms = 10000; +const int64_t Connector::endpoint_ping_diff_ms = 20; +const std::set Connector::multicast_types = { + message::Type::tInit, message::Type::tInitAck, message::Type::tPing +}; +const size_t Connector::PingHistory::history_length = 5; +const int64_t Connector::PingHistory::unavailable_ms = 100000; + +Connector::PingHistory::PingHistory() +: ping_sum(0) +, sent_id(0) +, sent_time(0) { + for (size_t i = 0; i < history_length; ++i) + AppendPing(unavailable_ms); +} + +void Connector::PingHistory::AppendPing(int64_t ms) { + if (history.size() >= history_length) { + ping_sum -= history.front(); + history.pop(); + } + if (history.size() < history_length) { + ping_sum += ms; + history.emplace(ms); + } +} + +void Connector::PingHistory::UpdatePing(int64_t ms) { + if (!history.empty()) { + ping_sum = ping_sum - history.back() + ms; + history.back() = ms; + } else + AppendPing(ms); +} + +void Connector::PingHistory::Ping(uint32_t id) { + sent_id = id; + sent_time = rtc::TimeMillis(); +} + +void Connector::PingHistory::Pong(uint32_t id) { + if (id != sent_id) + return; + sent_id = 0; + UpdatePing(std::min(rtc::TimeMillis() - sent_time, unavailable_ms)); + sent_time = 0; +} + +double Connector::PingHistory::Average() { + return static_cast(ping_sum) / history.size(); +} + +Connector::Connector(std::unique_ptr layer) +: active_endpoint(nullptr) +, thread(rtc::Thread::CreateWithSocketServer()) +, socket_factory(thread.get()) +, layer(std::move(layer)) +, ping_seq(0) { + pinger = webrtc::RepeatingTaskHandle::Start(thread.get(), [this]() { + Connector::UpdateActiveEndpoint(); + return webrtc::TimeDelta::ms(ping_interval_ms); + }); +} + +void Connector::Start() { + thread->Start(); + thread->Invoke(RTC_FROM_HERE, [this]() { + socket.reset(socket_factory.CreateUdpSocket( + rtc::SocketAddress(rtc::GetAnyIP(AF_INET), 0), 0, 0)); + socket->SignalReadPacket.connect(this, &Connector::RecvPacket); + socket->SignalReadyToSend.connect(this, &Connector::Ready); + }); +} + +void Connector::RecvPacket(rtc::AsyncPacketSocket *sock, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) { + for (const auto& ep : endpoints) { + auto ep_udp = dynamic_cast(ep.first); + if (ep_udp && ep_udp->address == remote_addr) { + ep_udp->RecvPacket(sock, data, len, remote_addr, packet_time_us); + break; + } + } +} + +void Connector::Ready(rtc::AsyncPacketSocket *) { + SignalMessage(message::Ready()); +} + +void Connector::AddEndpointRelayTcpObfuscated(const rtc::SocketAddress& addr, const Relay::PeerTag& peer_tag) { + thread->Invoke(RTC_FROM_HERE, [this, addr, peer_tag]() { + std::unique_ptr sock(socket_factory.CreateClientTcpSocket( + rtc::SocketAddress(rtc::GetAnyIP(AF_INET), 0), + addr, proxy_info, "", rtc::PacketSocketTcpOptions())); + AddEndpoint(std::make_unique(std::move(sock), peer_tag, layer.get())); + }); +} + +void Connector::AddEndpointRelayUdp(const rtc::SocketAddress& addr, const Relay::PeerTag& peer_tag) { + thread->Invoke(RTC_FROM_HERE, [this, addr, peer_tag]() { + assert(socket); + AddEndpoint(std::make_unique(addr, peer_tag, socket.get(), layer.get())); + }); +} + +void Connector::SetEndpointP2p(const rtc::SocketAddress& addr) { + thread->Invoke(RTC_FROM_HERE, [this, addr]() { + assert(socket); + if (auto ep = GetP2pEndpoint()) + DeleteEndpoint(ep); + AddEndpoint(std::make_unique(addr, socket.get(), layer.get())); + }); +} + +Connector::~Connector() { + thread->Invoke(RTC_FROM_HERE, [this]() { + pinger.Stop(); + active_endpoint = nullptr; + endpoints.clear(); + ping_history.clear(); + }); +} + +void Connector::RecvMessage(const message::Base& msg, EndpointBase *endpoint) { + if (msg.ID == message::tDisconnected && endpoint->type == EndpointBase::Type::RelayObfuscatedTcp) { + thread->PostDelayedTask(webrtc::ToQueuedTask([this, endpoint]() { + if (endpoints.find(endpoint) == endpoints.end()) + return; + auto final_ep = dynamic_cast(endpoint); + if (!final_ep) + return; + std::unique_ptr sock(socket_factory.CreateClientTcpSocket( + rtc::SocketAddress(rtc::GetAnyIP(AF_INET), 0), + final_ep->address, proxy_info, "", rtc::PacketSocketTcpOptions())); + final_ep->Reconnect(std::move(sock)); + }), tcp_reconnect_delay); + if (active_endpoint == endpoint) + ResetActiveEndpoint(); + return; + } + if (auto msg_ping = dynamic_cast(&msg)) { + message::Pong msg_pong; + msg_pong.id = msg_ping->id; + endpoint->SendMessage(msg_pong); + return; + } + if (auto msg_pong = dynamic_cast(&msg)) { + ping_history[endpoint].Pong(msg_pong->id); + return; + } + // fallback if no active endpoint set + if (!active_endpoint) + active_endpoint = endpoint; + SignalMessage(msg); +} + +void Connector::SendMessage(const message::Base& msg) { + if (!active_endpoint || multicast_types.find(msg.ID) != multicast_types.end()) { + for (const auto& ep : endpoints) { + ep.first->SendMessage(msg); + if (auto msg_ping = dynamic_cast(&msg)) + ping_history[ep.first].Ping(msg_ping->id); + } + return; + } + active_endpoint->SendMessage(msg); +} + +EndpointP2p *Connector::GetP2pEndpoint() const { + for (const auto& ep : endpoints) + if (auto ep_p2p = dynamic_cast(ep.first)) + return ep_p2p; + return nullptr; +} + +void Connector::AddEndpoint(std::unique_ptr endpoint) { + EndpointBase *ep = endpoint.get(); + ep->SignalMessage.connect(this, &Connector::RecvMessage); + endpoints[ep] = std::move(endpoint); + ping_history[ep] = PingHistory(); +} + +void Connector::DeleteEndpoint(EndpointBase *ep) { + // TODO: must be invoked to thread when become public + endpoints.erase(ep); + ping_history.erase(ep); +} + +void Connector::ResetActiveEndpoint() { + active_endpoint = nullptr; +} + +void Connector::UpdateActiveEndpoint() { + if (ping_history.empty()) + return; + if (ping_history.size() == 1) { + active_endpoint = ping_history.begin()->first; + return; + } + std::vector> times; + for (auto ping : ping_history) + times.emplace_back(ping.second.Average(), ping.first); + std::sort(times.begin(), times.end()); + EndpointBase *candidate = times.front().second; + if (!active_endpoint || (active_endpoint != candidate && + ping_history[active_endpoint].Average() - times.front().first > endpoint_ping_diff_ms)) + active_endpoint = candidate; + message::Ping msg; + msg.id = ++ping_seq; + SendMessage(msg); +} + +void Connector::SetProxy(rtc::ProxyType type, const rtc::SocketAddress& addr, const std::string& username, + const std::string& password) { + proxy_info.type = type; + proxy_info.address = addr; + proxy_info.username = username; + proxy_info.password = rtc::CryptString(); +} diff --git a/submodules/TgVoipWebrtc/Impl/Connector.h b/submodules/TgVoipWebrtc/Impl/Connector.h new file mode 100644 index 0000000000..4331460a27 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Connector.h @@ -0,0 +1,79 @@ +#ifndef DEMO_CONNECTOR_H +#define DEMO_CONNECTOR_H + + +#include "Endpoint.h" +#include "LayerBase.h" +#include "Message.h" + +#include "p2p/base/basic_packet_socket_factory.h" +#include "rtc_base/proxy_info.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" + +#include +#include + +class Connector : public sigslot::has_slots<> { +public: + explicit Connector(std::unique_ptr layer); + ~Connector() override; + void Start(); + void AddEndpointRelayTcpObfuscated(const rtc::SocketAddress& addr, const Relay::PeerTag& peer_tag); + void AddEndpointRelayUdp(const rtc::SocketAddress& addr, const Relay::PeerTag& peer_tag); + void SetEndpointP2p(const rtc::SocketAddress& addr); + sigslot::signal1 SignalMessage; + void SendMessage(const message::Base&); + void ResetActiveEndpoint(); + void SetProxy(rtc::ProxyType type, const rtc::SocketAddress& addr, const std::string& username, + const std::string& password); + +private: + class PingHistory { + public: + PingHistory(); + void Ping(uint32_t id); + void Pong(uint32_t id); + double Average(); + + private: + void AppendPing(int64_t ms); + void UpdatePing(int64_t ms); + + static const size_t history_length; + static const int64_t unavailable_ms; + + std::queue history; + int64_t ping_sum; + uint32_t sent_id; + int64_t sent_time; + }; + + static const int64_t tcp_reconnect_delay; + static const int64_t ping_interval_ms; + static const int64_t endpoint_ping_diff_ms; + static const std::set multicast_types; + + EndpointP2p *GetP2pEndpoint() const; + void AddEndpoint(std::unique_ptr); + void DeleteEndpoint(EndpointBase *ep); + void RecvPacket(rtc::AsyncPacketSocket *socket, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us); + void Ready(rtc::AsyncPacketSocket *); + void RecvMessage(const message::Base&, EndpointBase *); + void UpdateActiveEndpoint(); + + rtc::ProxyInfo proxy_info; + EndpointBase *active_endpoint; + std::unique_ptr thread; + rtc::BasicPacketSocketFactory socket_factory; + std::unique_ptr socket; + std::unique_ptr layer; + std::map> endpoints; + std::map ping_history; + webrtc::RepeatingTaskHandle pinger; + uint32_t ping_seq; +}; + +#endif //DEMO_CONNECTOR_H diff --git a/submodules/TgVoipWebrtc/Impl/Controller.cpp b/submodules/TgVoipWebrtc/Impl/Controller.cpp new file mode 100644 index 0000000000..b00c97819b --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Controller.cpp @@ -0,0 +1,225 @@ +#include "Controller.h" + +#include "Layer92.h" + +#include "modules/rtp_rtcp/source/rtp_utility.h" +#include "rtc_base/time_utils.cc" +#include "rtc_base/message_handler.h" + +#include + +std::map Controller::network_params = { + {message::NetworkType::nGprs, {6, 8, 6, 120, false, false, false}}, + {message::NetworkType::nEdge, {6, 16, 6, 120, false, false, false}}, + {message::NetworkType::n3gOrAbove, {6, 32, 16, 60, false, false, false}}, +}; +MediaEngineWebrtc::NetworkParams Controller::default_network_params = {6, 32, 16, 30, false, false, false}; +MediaEngineWebrtc::NetworkParams Controller::datasaving_network_params = {6, 8, 6, 120, false, false, true}; + +Controller::Controller(bool is_outgoing, const EncryptionKey& encryption_key, size_t init_timeout, size_t reconnect_timeout) +: thread(rtc::Thread::Create()) +, connector(std::make_unique(std::make_unique(encryption_key, is_outgoing))) +, state(State::Starting) +, is_outgoing(is_outgoing) +, last_recv_time(rtc::TimeMillis()) +, last_send_time(rtc::TimeMillis()) +, init_timeout(init_timeout * 1000) +, reconnect_timeout(reconnect_timeout * 1000) +, local_datasaving(false) +, final_datasaving(false) +, local_network_type(message::NetworkType::nUnknown) +, final_network_type(message::NetworkType::nUnknown) +{ + connector->SignalMessage.connect(this, &Controller::NewMessage); + thread->Start(); +} + +Controller::~Controller() { + thread->Invoke(RTC_FROM_HERE, [this]() { + media = nullptr; +#ifdef TGVOIP_PREPROCESSED_OUTPUT + preproc = nullptr; +#endif + connector = nullptr; + }); +} + +void Controller::AddEndpoint(const rtc::SocketAddress& address, const Relay::PeerTag &peer_tag, + Controller::EndpointType type) { + if (type == EndpointType::UDP) + connector->AddEndpointRelayUdp(address, peer_tag); + else if (type == EndpointType::TCP) + connector->AddEndpointRelayTcpObfuscated(address, peer_tag); + else if (type == EndpointType::P2P) + connector->SetEndpointP2p(address); +} + +void Controller::Start() { + last_recv_time = rtc::TimeMillis(); + connector->Start(); +} + +void Controller::NewMessage(const message::Base& msg) { + if (msg.ID == message::tReady && state == State::Starting) { + state = State::WaitInit; + SignalNewState(state); + StartRepeating([this]() { + message::Init msg; + msg.minVer = ProtocolBase::minimal_version; + msg.ver = ProtocolBase::actual_version; + connector->SendMessage(msg); + if (rtc::TimeMillis() - last_recv_time > init_timeout) + SetFail(); + return webrtc::TimeDelta::seconds(1); + }); + } else if ((msg.ID == message::tInit || msg.ID == message::tInitAck) && state == State::WaitInit) { + state = State::WaitInitAck; + SignalNewState(state); + StartRepeating([this]() { + message::InitAck msg; + // TODO: version matching + msg.minVer = ProtocolBase::minimal_version; + msg.ver = ProtocolBase::actual_version; + connector->SendMessage(msg); + if (rtc::TimeMillis() - last_recv_time > init_timeout) + SetFail(); + return webrtc::TimeDelta::seconds(1); + }); + } else if ((msg.ID == message::tInitAck || msg.ID == message::tRtpStream) && state == State::WaitInitAck) { + state = State::Established; + SignalNewState(state); + thread->PostTask(RTC_FROM_HERE, [this]() { +#ifdef TGVOIP_PREPROCESSED_OUTPUT + preproc = std::make_unique(not is_outgoing, false, true); + preproc->Play.connect(this, &Controller::Preprocessed); +#endif + media = std::make_unique(is_outgoing); + media->Record.connect(this, &Controller::Record); + media->Play.connect(this, &Controller::Play); + media->Send.connect(this, &Controller::SendRtp); + }); + StartRepeating([this]() { + if (state == State::Established && rtc::TimeMillis() - last_recv_time > 1000) { + connector->ResetActiveEndpoint(); + state = State::Reconnecting; + SignalNewState(state); + } else if (state == State::Reconnecting && rtc::TimeMillis() - last_recv_time > reconnect_timeout) + SetFail(); + return webrtc::TimeDelta::seconds(1); + }); + } if ((msg.ID == message::tRtpStream) && (state == State::Established || state == State::Reconnecting)) { + const auto msg_rtp = *dynamic_cast(&msg); + thread->PostTask(RTC_FROM_HERE, [this, msg_rtp]() { + if (media) { + media->Receive(msg_rtp.data); + UpdateNetworkParams(msg_rtp); + } + }); + if (!webrtc::RtpUtility::RtpHeaderParser(msg_rtp.data.data(), msg_rtp.data.size()).RTCP()) { + last_recv_time = rtc::TimeMillis(); + if (state == State::Reconnecting) { + state = State::Established; + SignalNewState(state); + } + } + } else if (msg.ID == message::tBufferOverflow || + msg.ID == message::tPacketIncorrect || + msg.ID == message::tWrongProtocol) { + SetFail(); + } +} + +template +void Controller::StartRepeating(Closure&& closure) { + StopRepeating(); + repeatable = webrtc::RepeatingTaskHandle::Start(thread.get(), std::forward(closure)); +} + +void Controller::StopRepeating() { + thread->Invoke(RTC_FROM_HERE, [this]() { + repeatable.Stop(); + }); +} + +void Controller::SetFail() { + thread->PostTask(RTC_FROM_HERE, [this]() { + media = nullptr; +#ifdef TGVOIP_PREPROCESSED_OUTPUT + preproc = nullptr; +#endif + }); + if (state != State::Failed) { + state = State::Failed; + SignalNewState(state); + } + StopRepeating(); +} + +void Controller::Play(const int16_t *data, size_t size) { + SignalPlay(data, size); +} + +void Controller::Record(int16_t *data, size_t size) { + SignalRecord(data, size); + last_send_time = rtc::TimeMillis(); +} + +#ifdef TGVOIP_PREPROCESSED_OUTPUT +void Controller::Preprocessed(const int16_t *data, size_t size) { + if (rtc::TimeMillis() - last_send_time < 100) + SignalPreprocessed(data, size); +} +#endif + +void Controller::SendRtp(rtc::CopyOnWriteBuffer packet) { +#ifdef TGVOIP_PREPROCESSED_OUTPUT + thread->PostTask(RTC_FROM_HERE, [this, packet]() { + if (preproc) + preproc->Receive(packet); + }); +#endif + message::RtpStream msg; + msg.data = packet; + msg.network_type = local_network_type; + msg.data_saving = local_datasaving; + connector->SendMessage(msg); +} + +void Controller::UpdateNetworkParams(const message::RtpStream& rtp) { + bool new_datasaving = local_datasaving || rtp.data_saving; + if (!new_datasaving) { + final_datasaving = false; + message::NetworkType new_network_type = std::min(local_network_type, rtp.network_type); + if (new_network_type != final_network_type) { + final_network_type = new_network_type; + auto it = network_params.find(rtp.network_type); + if (it == network_params.end()) + media->SetNetworkParams(default_network_params); + else + media->SetNetworkParams(it->second); + } + } else if (new_datasaving != final_datasaving) { + final_datasaving = true; + media->SetNetworkParams(datasaving_network_params); + } +} + +void Controller::SetNetworkType(message::NetworkType network_type) { + local_network_type = network_type; +} + +void Controller::SetDataSaving(bool data_saving) { + local_datasaving = data_saving; +} + +void Controller::SetMute(bool mute) { + thread->Invoke(RTC_FROM_HERE, [this, mute]() { + if (media) + media->SetMute(mute); + }); +} + +void Controller::SetProxy(rtc::ProxyType type, const rtc::SocketAddress& addr, const std::string& username, + const std::string& password) { + connector->SetProxy(type, addr, username, password); +} diff --git a/submodules/TgVoipWebrtc/Impl/Controller.h b/submodules/TgVoipWebrtc/Impl/Controller.h new file mode 100644 index 0000000000..ebd1a7d27c --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Controller.h @@ -0,0 +1,84 @@ +#ifndef DEMO_CONTROLLER_H +#define DEMO_CONTROLLER_H + + +#include "Connector.h" +#include "MediaEngineWebrtc.h" +#include "Layer92.h" + +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +class Controller : public sigslot::has_slots<> { +public: + enum EndpointType { + UDP, + TCP, + P2P, + }; + + enum State { + Starting, + WaitInit, + WaitInitAck, + Established, + Failed, + Reconnecting, + }; + + explicit Controller(bool is_outgoing, const EncryptionKey& encryption_key, size_t init_timeout, size_t reconnect_timeout); + ~Controller() override; + void AddEndpoint(const rtc::SocketAddress& address, const Relay::PeerTag& peer_tag, EndpointType type); + void Start(); + void SetNetworkType(message::NetworkType network_type); + void SetDataSaving(bool data_saving); + void SetMute(bool mute); + void SetProxy(rtc::ProxyType type, const rtc::SocketAddress& addr, const std::string& username, + const std::string& password); + + static std::map network_params; + static MediaEngineWebrtc::NetworkParams default_network_params; + static MediaEngineWebrtc::NetworkParams datasaving_network_params; + sigslot::signal2 SignalRecord; +#ifdef TGVOIP_PREPROCESSED_OUTPUT + sigslot::signal2 SignalPreprocessed; +#endif + sigslot::signal2 SignalPlay; + sigslot::signal1 SignalNewState; + +private: + std::unique_ptr thread; + std::unique_ptr connector; + std::unique_ptr media; +#ifdef TGVOIP_PREPROCESSED_OUTPUT + std::unique_ptr preproc; +#endif + State state; + webrtc::RepeatingTaskHandle repeatable; + int64_t last_recv_time; + int64_t last_send_time; + const bool is_outgoing; + const size_t init_timeout; + const size_t reconnect_timeout; + bool local_datasaving; + bool final_datasaving; + message::NetworkType local_network_type; + message::NetworkType final_network_type; + + template void StartRepeating(Closure&& closure); + void StopRepeating(); + void NewMessage(const message::Base& msg); + void SetFail(); + void Play(const int16_t *data, size_t size); + void Record(int16_t *data, size_t size); +#ifdef TGVOIP_PREPROCESSED_OUTPUT + void Preprocessed(const int16_t *data, size_t size); +#endif + void SendRtp(rtc::CopyOnWriteBuffer packet); + void UpdateNetworkParams(const message::RtpStream& rtp); +}; + + +#endif //DEMO_CONTROLLER_H diff --git a/submodules/TgVoipWebrtc/Impl/Endpoint.cpp b/submodules/TgVoipWebrtc/Impl/Endpoint.cpp new file mode 100644 index 0000000000..4e26c0d941 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Endpoint.cpp @@ -0,0 +1,204 @@ +#include "Endpoint.h" + +#include "rtc_base/buffer.h" +#include "rtc_base/byte_buffer.h" + +#include +#include + +EndpointBase::EndpointBase(LayerBase *layer, Type type) +: type(type) +, layer(layer) +, in_buffer(std::make_unique(nullptr, 0)) +, in_remains(0) {} + +void EndpointBase::RecvPacket(rtc::AsyncPacketSocket *, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) { + if (in_buffer && in_buffer->Length() > 0) { + rtc::Buffer tmp(in_buffer->Data(), in_buffer->Length() + len); + memcpy(tmp.data() + in_buffer->Length(), data, len); + in_buffer = std::make_unique(reinterpret_cast(tmp.data()), tmp.size()); + } else + in_buffer = std::make_unique(data, len); +} + +EndpointUdp::EndpointUdp(const rtc::SocketAddress& addr, rtc::AsyncPacketSocket *socket, LayerBase *layer, Type type) +: EndpointBase(layer, type) +, address(addr) +, socket(socket) {} + +void EndpointUdp::SendPacket(const uint8_t *data, size_t size) { + socket->SendTo(data, size, address, packet_options); +} + +EndpointTcp::EndpointTcp(std::unique_ptr socket, LayerBase *layer, Type type) +: EndpointBase(layer, type) +, address(socket->GetRemoteAddress()) +, socket(nullptr) { + Reconnect(std::move(socket)); +} + +void EndpointTcp::Reconnect(std::unique_ptr socket_) { + socket = std::move(socket_); + socket->SignalReadPacket.connect(dynamic_cast(this), &EndpointTcp::RecvPacket); + socket->SignalReadyToSend.connect(this, &EndpointTcp::Ready); + socket->SignalClose.connect(this, &EndpointTcp::Close); +} + +void EndpointTcp::SendPacket(const uint8_t *data, size_t size) { + socket->Send(data, size, packet_options); +} + +Relay::Relay(const PeerTag& peer_tag_) : peer_tag() { + memcpy(peer_tag, peer_tag_, sizeof(PeerTag)); +} + +bool Relay::CheckPacket(rtc::ByteBufferReader *packet) { + if (packet->Length() >= 16 && memcmp(peer_tag, packet->Data(), 16) == 0) { + packet->Consume(16); + return true; + } + return false; +} + +const rtc::Buffer& Relay::PreparePacket(const rtc::Buffer& packet) { + buffer.Clear(); + if (!packet.empty()) { + buffer.AppendData(peer_tag, 16); + buffer.AppendData(packet); + } + return buffer; +} + +EndpointRelayObfuscatedTcp::EndpointRelayObfuscatedTcp(std::unique_ptr socket, + const PeerTag& peer_tag, LayerBase *layer) +: Relay(peer_tag) +, EndpointTcp(std::move(socket), layer, Type::RelayObfuscatedTcp) +, recvState() +, sendState() {} + +void EndpointRelayObfuscatedTcp::Ready(rtc::AsyncPacketSocket *) { + unsigned char buf[64]; + layer->GenerateTCPO2States(buf, &recvState, &sendState); + EndpointTcp::SendPacket(buf, 64); + SignalMessage(message::Connected(), this); +} + +void EndpointRelayObfuscatedTcp::Close(rtc::AsyncPacketSocket *, int) { + SignalMessage(message::Disconnected(), this); +} + +void EndpointRelayObfuscatedTcp::RecvPacket(rtc::AsyncPacketSocket *socket, const char *data, size_t packet_len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) { + EndpointBase::RecvPacket(socket, data, packet_len, remote_addr, packet_time_us); + do { + if (in_remains > in_buffer->Length()) + break; + if (in_remains > 0 && CheckPacket(in_buffer.get())) { + auto msg = layer->DecodePacket(*in_buffer); + if (msg) + SignalMessage(*msg, this); + } + + unsigned char len1; + size_t packetLen = 0; + if (!in_buffer->ReadUInt8(&len1)) + break; + layer->EncryptForTCPO2(&len1, 1, &recvState); + if (len1 < 0x7F) { + packetLen = (size_t) len1 * 4; + } else { + unsigned char len2[3]; + if (!in_buffer->ReadBytes(reinterpret_cast(&len2), 3)) { + SignalMessage(message::PacketIncorrect(), this); + return; + } + layer->EncryptForTCPO2(len2, 3, &recvState); + packetLen = ((size_t) len2[0] | ((size_t) len2[1] << 8) | ((size_t) len2[2] << 16)) * 4; + } + + in_remains = packetLen; + if (packetLen > in_buffer->Length()) { + in_remains = packetLen; + break; + } + } while (true); +} + +void EndpointRelayObfuscatedTcp::SendMessage(const message::Base& msg_base) { + if (socket->GetState() == rtc::AsyncPacketSocket::State::STATE_CLOSED) + return; + const rtc::Buffer& out = PreparePacket(layer->EncodePacket(&msg_base)); + if (!out.empty()) + SendPacket(out.data(), out.size()); +} + +void EndpointRelayObfuscatedTcp::SendPacket(const uint8_t *data, size_t size) { + rtc::ByteBufferWriter out; + size_t len = size / 4; + if (len < 0x7F) { + out.WriteUInt8(len); + } else { + out.WriteUInt8(0x7F); + out.WriteUInt8(len & 0xFF); + out.WriteUInt8((len >> 8) & 0xFF); + out.WriteUInt8((len >> 16) & 0xFF); + } + out.WriteBytes(reinterpret_cast(data), size); + layer->EncryptForTCPO2(reinterpret_cast(out.ReserveWriteBuffer(0)), + out.Length(), &sendState); + EndpointTcp::SendPacket(reinterpret_cast(out.Data()), out.Length()); +} + +EndpointRelayUdp::EndpointRelayUdp(const rtc::SocketAddress& addr, const PeerTag& peer_tag, + rtc::AsyncPacketSocket *socket, LayerBase *layer) + : Relay(peer_tag) + , EndpointUdp(addr, socket, layer, Type::RelayUdp) {} + +void EndpointRelayUdp::SendMessage(const message::Base& msg_base) { + const rtc::Buffer& out = PreparePacket(layer->EncodePacket(&msg_base)); + if (!out.empty()) + SendPacket(out.data(), out.size()); +} + +void EndpointRelayUdp::RecvPacket(rtc::AsyncPacketSocket *sock, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) { + bool glued; + bool processed = false; + do { + EndpointBase::RecvPacket(sock, data, len, remote_addr, packet_time_us); + glued = in_buffer->Length() > len; + std::unique_ptr msg; + while (CheckPacket(in_buffer.get()) && (msg = layer->DecodePacket(*in_buffer))) { + processed = true; + SignalMessage(*msg, this); + } + if (!processed) + in_buffer = std::make_unique(nullptr, 0); + } while (!processed && glued); +} + +EndpointP2p::EndpointP2p(const rtc::SocketAddress& addr, rtc::AsyncPacketSocket *socket, LayerBase *layer) +: EndpointUdp(addr, socket, layer, Type::P2p) {} + +void EndpointP2p::SendMessage(const message::Base& msg_base) { + rtc::Buffer out = layer->EncodePacket(&msg_base); + if (!out.empty()) + SendPacket(out.data(), out.size()); +} + +void EndpointP2p::RecvPacket(rtc::AsyncPacketSocket *sock, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) { + bool glued; + bool processed = false; + do { + EndpointBase::RecvPacket(sock, data, len, remote_addr, packet_time_us); + glued = in_buffer->Length() > len; + while (auto msg = layer->DecodePacket(*in_buffer)) { + processed = true; + SignalMessage(*msg, this); + } + if (!processed) + in_buffer = std::make_unique(nullptr, 0); + } while (!processed && glued); +} diff --git a/submodules/TgVoipWebrtc/Impl/Endpoint.h b/submodules/TgVoipWebrtc/Impl/Endpoint.h new file mode 100644 index 0000000000..3d6593aeec --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Endpoint.h @@ -0,0 +1,126 @@ +#ifndef DEMO_ENDPOINT_H +#define DEMO_ENDPOINT_H + + +#include "LayerBase.h" +#include "Message.h" + +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/buffer_queue.h" +#include "rtc_base/socket_address.h" + +class Connector; + +class EndpointBase : public sigslot::has_slots<> { +public: + enum Type { + Unknown, + RelayUdp, + RelayObfuscatedTcp, + P2p, + }; + + EndpointBase(const EndpointBase&) = delete; + virtual void SendMessage(const message::Base&) = 0; + sigslot::signal2 SignalMessage; + const Type type; + +protected: + explicit EndpointBase(LayerBase *layer, Type type); + virtual void RecvPacket(rtc::AsyncPacketSocket *socket, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us); + virtual void SendPacket(const uint8_t *data, size_t size) = 0; + + LayerBase *layer; + rtc::PacketOptions packet_options; + std::unique_ptr in_buffer; + size_t in_remains; +}; + +class Relay { +public: + typedef unsigned char PeerTag[16]; + + virtual ~Relay() = default; + +protected: + explicit Relay(const PeerTag& peer_tag); + bool CheckPacket(rtc::ByteBufferReader *packet); + const rtc::Buffer& PreparePacket(const rtc::Buffer& packet); + + PeerTag peer_tag; // how to initialize it in initializer list? + +private: + rtc::Buffer buffer; +}; + +class EndpointUdp : public EndpointBase { +public: + const rtc::SocketAddress address; + +protected: + friend class Connector; +// friend void Connector::RecvPacket(rtc::AsyncPacketSocket *, const char *, size_t, +// const rtc::SocketAddress&, const int64_t&); + + EndpointUdp(const rtc::SocketAddress& addr, rtc::AsyncPacketSocket *socket, LayerBase *layer, Type type); + void SendPacket(const uint8_t *data, size_t size) override; + + rtc::AsyncPacketSocket *socket; +}; + +class EndpointTcp : public EndpointBase { +public: + const rtc::SocketAddress address; + + void Reconnect(std::unique_ptr socket_); + +protected: + explicit EndpointTcp(std::unique_ptr socket, LayerBase *layer, Type type); + void SendPacket(const uint8_t *data, size_t size) override; + + virtual void Ready(rtc::AsyncPacketSocket *) = 0; + virtual void Close(rtc::AsyncPacketSocket *, int) = 0; + + std::unique_ptr socket; +}; + +class EndpointRelayObfuscatedTcp final : public Relay, public EndpointTcp { +public: + EndpointRelayObfuscatedTcp(std::unique_ptr socket, const PeerTag& peer_tag, + LayerBase *layer); + void SendMessage(const message::Base&) override; + +private: + void RecvPacket(rtc::AsyncPacketSocket *socket, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) override; + void Ready(rtc::AsyncPacketSocket *) override; + void Close(rtc::AsyncPacketSocket *, int) override; + void SendPacket(const uint8_t *data, size_t size) override; + + TCPO2State recvState; + TCPO2State sendState; +}; + +class EndpointRelayUdp final : public Relay, public EndpointUdp { +public: + EndpointRelayUdp(const rtc::SocketAddress& addr, const PeerTag& peer_tag, + rtc::AsyncPacketSocket *socket, LayerBase *layer); + void SendMessage(const message::Base&) override; + +private: + void RecvPacket(rtc::AsyncPacketSocket *socket, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) override; +}; + +class EndpointP2p final : public EndpointUdp { +public: + EndpointP2p(const rtc::SocketAddress& addr, rtc::AsyncPacketSocket *socket, LayerBase *layer); + void SendMessage(const message::Base&) override; + +private: + void RecvPacket(rtc::AsyncPacketSocket *socket, const char *data, size_t len, + const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) override; +}; + +#endif //DEMO_ENDPOINT_H diff --git a/submodules/TgVoipWebrtc/Impl/Layer92.cpp b/submodules/TgVoipWebrtc/Impl/Layer92.cpp new file mode 100644 index 0000000000..8bea191d6b --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Layer92.cpp @@ -0,0 +1,238 @@ +#include "Layer92.h" + +#include "Message.h" + +#include "rtc_base/byte_buffer.h" +#include "rtc_base/byte_order.h" + +#include +#include + +namespace { + +#define TLID_UDP_REFLECTOR_SELF_INFO 0xc01572c7 +#define TLID_UDP_REFLECTOR_PEER_INFO 0x27D9371C + +#ifdef _MSC_VER +#define MSC_STACK_FALLBACK(a, b) (b) +#else +#define MSC_STACK_FALLBACK(a, b) (a) +#endif + +} + +Layer92::Layer92(const EncryptionKey& encryptionKey_, bool isOutgoing) +: LayerBase(92) +, encryptionKey() +, isOutgoing(isOutgoing) { + memcpy(encryptionKey, encryptionKey_, sizeof(encryptionKey)); +} + +void Layer92::EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2State *state) { + crypto.aes_ctr_encrypt(buffer, len, state->key, state->iv, state->ecount, &state->num); +} + +void Layer92::GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState) { + memset(recvState, 0, sizeof(TCPO2State)); + memset(sendState, 0, sizeof(TCPO2State)); + unsigned char nonce[64]; + uint32_t *first = reinterpret_cast(nonce), *second = first + 1; + uint32_t first1 = 0x44414548U, first2 = 0x54534f50U, first3 = 0x20544547U, first4 = 0x20544547U, first5 = 0xeeeeeeeeU; + uint32_t second1 = 0; + do { + crypto.rand_bytes(nonce, sizeof(nonce)); + } while (*first == first1 || *first == first2 || *first == first3 || *first == first4 || *first == first5 || + *second == second1 || *reinterpret_cast(nonce) == 0xef); + + // prepare encryption key/iv + memcpy(sendState->key, nonce + 8, 32); + memcpy(sendState->iv, nonce + 8 + 32, 16); + + // prepare decryption key/iv + char reversed[48]; + memcpy(reversed, nonce + 8, sizeof(reversed)); + std::reverse(reversed, reversed + sizeof(reversed)); + memcpy(recvState->key, reversed, 32); + memcpy(recvState->iv, reversed + 32, 16); + + // write protocol identifier + *reinterpret_cast(nonce + 56) = 0xefefefefU; + memcpy(buffer, nonce, 56); + EncryptForTCPO2(nonce, sizeof(nonce), sendState); + memcpy(buffer + 56, nonce + 56, 8); +} + +std::unique_ptr Layer92::DecodeRelayPacket(rtc::ByteBufferReader& in) { + if (in.Length() < 12 + 4 + 16) + return nullptr; + if (*reinterpret_cast(in.Data()) != 0xFFFFFFFFFFFFFFFFLL) + return nullptr; + if (*reinterpret_cast(in.Data() + 8) != 0xFFFFFFFF) + return nullptr; + + // relay special request response + in.Consume(12); + uint32_t tlid; + if (!in.ReadUInt32(&tlid)) + return nullptr; + + if (tlid == TLID_UDP_REFLECTOR_SELF_INFO) { + if (in.Length() < 32) + return nullptr; + + auto msg = std::make_unique(); + in.ReadUInt32(&msg->date); + in.ReadUInt64(&msg->query_id); + in6_addr myIP{}; + in.ReadBytes(reinterpret_cast(&myIP), 16); + uint32_t myPort; // int32_t in src; why not uint16_t? + in.ReadUInt32(&myPort); + msg->my_addr = rtc::SocketAddress(rtc::IPAddress(myIP), myPort); + return msg; + } + if (tlid == TLID_UDP_REFLECTOR_PEER_INFO) { + if (in.Length() < 16) + return nullptr; + auto msg = std::make_unique(); + uint32_t myAddr; + uint32_t myPort; + uint32_t peerAddr; + uint32_t peerPort; + in.ReadUInt32(&myAddr); + in.ReadUInt32(&myPort); + in.ReadUInt32(&peerAddr); + in.ReadUInt32(&peerPort); + msg->my_addr = rtc::SocketAddress(myAddr, myPort); + msg->peer_addr = rtc::SocketAddress(peerAddr, peerPort); + return msg; + } + return nullptr; +} + +void Layer92::KDF2(unsigned char *msgKey, size_t x, unsigned char *aesKey, unsigned char *aesIv) { + uint8_t sA[32], sB[32]; + uint8_t buf[16 + 36]; + memcpy(buf, msgKey, 16); + memcpy(buf + 16, encryptionKey + x, 36); + crypto.sha256(buf, 16 + 36, sA); + memcpy(buf, encryptionKey + 40 + x, 36); + memcpy(buf + 36, msgKey, 16); + crypto.sha256(buf, 36 + 16, sB); + memcpy(aesKey, sA, 8); + memcpy(aesKey + 8, sB + 8, 16); + memcpy(aesKey + 8 + 16, sA + 24, 8); + memcpy(aesIv, sB, 8); + memcpy(aesIv + 8, sA + 8, 16); + memcpy(aesIv + 8 + 16, sB + 24, 8); +} + +std::unique_ptr Layer92::DecodeProtocolPacket(rtc::ByteBufferReader& in) { + unsigned char msgKey[16]; + memcpy(msgKey, in.Data(), 16); + + unsigned char decrypted[1500]; + unsigned char aesKey[32], aesIv[32]; + KDF2(msgKey, isOutgoing ? 8 : 0, aesKey, aesIv); + size_t decryptedLen = in.Length() - 16; + if (decryptedLen > sizeof(decrypted)) + return nullptr; + if (decryptedLen % 16 != 0) + return nullptr; // wrong decrypted length + + in.Consume(16); + crypto.aes_ige_decrypt((uint8_t *)in.Data(), decrypted, decryptedLen, aesKey, aesIv); + in.Consume(decryptedLen); + + rtc::ByteBufferWriter buf; + size_t x = isOutgoing ? 8 : 0; + buf.WriteBytes((char *)encryptionKey + 88 + x, 32); + buf.WriteBytes((char *)decrypted, decryptedLen); + unsigned char msgKeyLarge[32]; + crypto.sha256((uint8_t *)buf.Data(), buf.Length(), msgKeyLarge); + if (memcmp(msgKey, msgKeyLarge + 8, 16) != 0) + return nullptr; // packet has wrong hash + + uint16_t innerLen; + memcpy(&innerLen, decrypted, 2); + if (innerLen > decryptedLen) + return nullptr; // packet has wrong inner length +// if (decryptedLen - innerLen < 16) +// return nullptr; // packet has too little padding + return protocol->ReadProtocolPacket(decrypted + 2, innerLen); +} + +std::unique_ptr Layer92::DecodePacket(rtc::ByteBufferReader& in) { + auto msg = DecodeRelayPacket(in); + if (msg) + return msg; + return DecodeProtocolPacket(in); +} + +rtc::Buffer Layer92::EncodePacket(const message::Base *msg_base) { + auto buf = EncodeRelayPacket(msg_base); + if (!buf.empty()) + return buf; + return EncodeProtocolPacket(msg_base); +} + +rtc::Buffer Layer92::EncodeRelayPacket(const message::Base *msg_base) { + if (msg_base->ID == message::tRelayPing) { + const auto *msg = dynamic_cast(msg_base); + if (!msg) + return rtc::Buffer(); + unsigned char buf[16]; + memset(buf, 0xFF, 16); + return rtc::Buffer(buf, 16); + } + if (msg_base->ID == message::tGetPeerInfo) { + const auto *msg = dynamic_cast(msg_base); + if (!msg) + return rtc::Buffer(); + rtc::ByteBufferWriter out; + out.WriteUInt32(-1); + out.WriteUInt32(-1); + out.WriteUInt32(-1); + out.WriteUInt32(-1); + int64_t id; + crypto.rand_bytes(reinterpret_cast(&id), 8); + out.WriteUInt64(id); + return rtc::Buffer(out.Data(), out.Length()); + } + return rtc::Buffer(); +} + +rtc::Buffer Layer92::EncodeProtocolPacket(const message::Base *msg_base) { + rtc::Buffer internal = protocol->WriteProtocolPacket(msg_base); + if (internal.empty()) + return rtc::Buffer(); + + rtc::ByteBufferWriter out; + rtc::ByteBufferWriter inner; + uint16_t len = internal.size(); + inner.WriteBytes((char *)&len, 2); // for backward compatibility + inner.WriteBytes((char *)internal.data(), internal.size()); + + size_t padLen = 16 - inner.Length() % 16; +// if (padLen < 16) +// padLen += 16; + uint8_t padding[32]; + crypto.rand_bytes(padding, padLen); + inner.WriteBytes((char *)padding, padLen); + assert(inner.Length() % 16 == 0); + + unsigned char key[32], iv[32], msgKey[16]; + rtc::ByteBufferWriter buf; + size_t x = isOutgoing ? 0 : 8; + buf.WriteBytes((char *)encryptionKey + 88 + x, 32); + buf.WriteBytes(inner.Data(), inner.Length()); + unsigned char msgKeyLarge[32]; + crypto.sha256((uint8_t *)buf.Data(), buf.Length(), msgKeyLarge); + memcpy(msgKey, msgKeyLarge + 8, 16); + KDF2(msgKey, isOutgoing ? 0 : 8, key, iv); + out.WriteBytes((char *)msgKey, 16); + + unsigned char aesOut[MSC_STACK_FALLBACK(inner.Length(), 1500)]; + crypto.aes_ige_encrypt((uint8_t *)inner.Data(), aesOut, inner.Length(), key, iv); + out.WriteBytes((char *)aesOut, inner.Length()); + return rtc::Buffer(out.Data(), out.Length()); +} diff --git a/submodules/TgVoipWebrtc/Impl/Layer92.h b/submodules/TgVoipWebrtc/Impl/Layer92.h new file mode 100644 index 0000000000..f751a95b12 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Layer92.h @@ -0,0 +1,49 @@ +#ifndef DEMO_LAYER92_H +#define DEMO_LAYER92_H + + +#include "LayerBase.h" +#include "Message.h" +#include "Protocol10.h" + +#include "rtc_base/byte_buffer.h" + +#include +#include + +struct CryptoFunctions { + void (*rand_bytes)(uint8_t* buffer, size_t length); + void (*sha1)(uint8_t* msg, size_t length, uint8_t* output); + void (*sha256)(uint8_t* msg, size_t length, uint8_t* output); + void (*aes_ige_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); + void (*aes_ige_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); + void (*aes_ctr_encrypt)(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num); + void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); + void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); +}; + +typedef unsigned char EncryptionKey[256]; + +class Layer92 : public LayerBase { +public: + static CryptoFunctions crypto; + + explicit Layer92(const EncryptionKey& encryptionKey, bool isOutgoing); + void EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2State *state) override; + void GenerateTCPO2States(unsigned char *buffer, TCPO2State *recvState, TCPO2State *sendState) override; + std::unique_ptr DecodePacket(rtc::ByteBufferReader& in) override; + rtc::Buffer EncodePacket(const message::Base *msg_base) override; + +private: + void KDF2(unsigned char* msgKey, size_t x, unsigned char *aesKey, unsigned char *aesIv); + std::unique_ptr DecodeRelayPacket(rtc::ByteBufferReader& in); + std::unique_ptr DecodeProtocolPacket(rtc::ByteBufferReader& in); + rtc::Buffer EncodeRelayPacket(const message::Base *msg_base); + rtc::Buffer EncodeProtocolPacket(const message::Base *msg_base); + + EncryptionKey encryptionKey; + bool isOutgoing; +}; + + +#endif //DEMO_LAYER92_H diff --git a/submodules/TgVoipWebrtc/Impl/LayerBase.cpp b/submodules/TgVoipWebrtc/Impl/LayerBase.cpp new file mode 100644 index 0000000000..9e6dbab457 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/LayerBase.cpp @@ -0,0 +1,17 @@ +#include "LayerBase.h" + +#include "Layer92.h" + +bool LayerBase::ChangeProtocol(uint32_t protocol_version) { + if (protocol && protocol->version == protocol_version) + return true; + auto new_protocol = ProtocolBase::CreateProtocol(protocol_version); + if (!new_protocol) + return false; + protocol = std::move(new_protocol); + return true; +} + +LayerBase::LayerBase(uint32_t version) +: version(version) +, protocol(ProtocolBase::CreateProtocol(ProtocolBase::actual_version)) {} diff --git a/submodules/TgVoipWebrtc/Impl/LayerBase.h b/submodules/TgVoipWebrtc/Impl/LayerBase.h new file mode 100644 index 0000000000..5d2db961d4 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/LayerBase.h @@ -0,0 +1,38 @@ +#ifndef DEMO_LAYERBASE_H +#define DEMO_LAYERBASE_H + + +#include "ProtocolBase.h" + +#include "rtc_base/buffer.h" +#include "rtc_base/byte_buffer.h" + +#include +#include + +struct TCPO2State { + unsigned char key[32]; + unsigned char iv[16]; + unsigned char ecount[16]; + uint32_t num; +}; + +class LayerBase { +public: + bool ChangeProtocol(uint32_t protocol_version); + + virtual ~LayerBase() = default; + virtual void EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2State *state) = 0; + virtual void GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState) = 0; + virtual std::unique_ptr DecodePacket(rtc::ByteBufferReader& in) = 0; + virtual rtc::Buffer EncodePacket(const message::Base *msg_base) = 0; + + const uint32_t version; + +protected: + explicit LayerBase(uint32_t version); + + std::unique_ptr protocol; +}; + +#endif //DEMO_LAYERBASE_H diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineBase.h b/submodules/TgVoipWebrtc/Impl/MediaEngineBase.h new file mode 100644 index 0000000000..d85aa8c785 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineBase.h @@ -0,0 +1,21 @@ +#ifndef DEMO_MEDIAENGINEBASE_H +#define DEMO_MEDIAENGINEBASE_H + + +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +#include + +class MediaEngineBase { +public: + MediaEngineBase() = default; + virtual ~MediaEngineBase() = default; + + sigslot::signal1 Send; + virtual void Receive(rtc::CopyOnWriteBuffer) = 0; + sigslot::signal2 Play; + sigslot::signal2 Record; +}; + +#endif //DEMO_MEDIAENGINEBASE_H diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp new file mode 100644 index 0000000000..4b6651c9a3 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp @@ -0,0 +1,204 @@ +#include "MediaEngineWebrtc.h" + +#include "api/audio_codecs/audio_decoder_factory_template.h" +#include "api/audio_codecs/audio_encoder_factory_template.h" +#include "api/audio_codecs/opus/audio_decoder_opus.h" +#include "api/audio_codecs/opus/audio_encoder_opus.h" +#include "api/rtp_parameters.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "media/base/codec.h" +#include "media/base/media_constants.h" +#include "media/engine/webrtc_media_engine.h" +#include "modules/audio_device/include/audio_device_default.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "system_wrappers/include/field_trial.h" + +#if WEBRTC_ENABLE_PROTOBUF +#include "modules/audio_coding/audio_network_adaptor/config.pb.h" +#endif + +namespace { +const size_t frame_samples = 480; +const uint8_t channels = 1; +const uint8_t sample_bytes = 2; +const uint32_t clockrate = 48000; +const uint16_t sdp_payload = 111; +const char* sdp_name = "opus"; +const uint8_t sdp_channels = 2; +const uint32_t sdp_bitrate = 0; +const uint32_t caller_ssrc = 1; +const uint32_t called_ssrc = 2; +const int extension_sequence = 1; +} + +MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) +: ssrc_send(outgoing ? caller_ssrc : called_ssrc) +, ssrc_recv(outgoing ? called_ssrc : caller_ssrc) +, event_log(std::make_unique()) +, task_queue_factory(webrtc::CreateDefaultTaskQueueFactory()) +, data_sender(*this) { + webrtc::field_trial::InitFieldTrialsFromString( + "WebRTC-Audio-SendSideBwe/Enabled/" + "WebRTC-Audio-Allocation/min:6kbps,max:32kbps/" + "WebRTC-Audio-OpusMinPacketLossRate/Enabled-1/" +// "WebRTC-Audio-OpusPlcUsePrevDecodedSamples/Enabled/" +// "WebRTC-Audio-NewOpusPacketLossRateOptimization/Enabled-1-20-1.0/" +// "WebRTC-SendSideBwe-WithOverhead/Enabled/" +// "WebRTC-Bwe-SeparateAudioPackets/enabled:true,packet_threshold:15,time_threshold:1000ms/" +// "WebRTC-Audio-AlrProbing/Disabled/" + ); + cricket::MediaEngineDependencies media_deps; + media_deps.task_queue_factory = task_queue_factory.get(); +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + media_deps.adm = new rtc::RefCountedObject>(); +#endif + media_deps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory(); + media_deps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory(); + media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create(); + media_engine = cricket::CreateMediaEngine(std::move(media_deps)); + media_engine->Init(); + webrtc::Call::Config call_config(event_log.get()); + call_config.task_queue_factory = task_queue_factory.get(); + call_config.trials = &field_trials; + call_config.audio_state = media_engine->voice().GetAudioState(); + call.reset(webrtc::Call::Create(call_config)); +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + audio_processor = std::make_unique(call_config.audio_state->audio_transport(), + task_queue_factory.get(), *this, send, recv); +#endif + voice_channel.reset(media_engine->voice().CreateMediaChannel( + call.get(), cricket::MediaConfig(), cricket::AudioOptions(), webrtc::CryptoOptions::NoGcm())); + if (send) { + voice_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send)); + SetNetworkParams({6, 32, 6, 120, false, false, false}); + SetMute(false); + voice_channel->SetInterface(&data_sender, webrtc::MediaTransportConfig()); + voice_channel->OnReadyToSend(true); + voice_channel->SetSend(true); + } + if (recv) { + cricket::AudioRecvParameters recv_parameters; + recv_parameters.codecs.emplace_back(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); + recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); + recv_parameters.rtcp.reduced_size = true; + recv_parameters.rtcp.remote_estimate = true; + voice_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv)); + voice_channel->SetRecvParameters(recv_parameters); + voice_channel->SetPlayout(true); + } +} + +MediaEngineWebrtc::~MediaEngineWebrtc() = default; + +void MediaEngineWebrtc::Receive(rtc::CopyOnWriteBuffer packet) { + if (voice_channel) + voice_channel->OnPacketReceived(packet, -1); +} + +void MediaEngineWebrtc::OnSentPacket(const rtc::SentPacket& sent_packet) { + call->OnSentPacket(sent_packet); +} + +void MediaEngineWebrtc::SetNetworkParams(const MediaEngineWebrtc::NetworkParams& params) { + cricket::AudioCodec opus_codec(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); + opus_codec.AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc)); + opus_codec.SetParam(cricket::kCodecParamMinBitrate, params.min_bitrate_kbps); + opus_codec.SetParam(cricket::kCodecParamStartBitrate, params.start_bitrate_kbps); + opus_codec.SetParam(cricket::kCodecParamMaxBitrate, params.max_bitrate_kbps); + opus_codec.SetParam(cricket::kCodecParamUseInbandFec, 1); + opus_codec.SetParam(cricket::kCodecParamPTime, params.ptime_ms); +// opus_codec.SetParam(cricket::kCodecParamUseDtx, "1"); +// opus_codec.SetParam(cricket::kCodecParamMaxAverageBitrate, 6); + std::string config_string; +#if WEBRTC_ENABLE_PROTOBUF + webrtc::audio_network_adaptor::config::ControllerManager cont_conf; +// cont_conf.add_controllers()->mutable_bitrate_controller(); + config_string = cont_conf.SerializeAsString(); +#endif + cricket::AudioSendParameters send_parameters; + if (!config_string.empty()) { + send_parameters.options.audio_network_adaptor_config = config_string; + send_parameters.options.audio_network_adaptor = true; + } + send_parameters.codecs.push_back(opus_codec); + send_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); + send_parameters.options.echo_cancellation = params.echo_cancellation; +// send_parameters.options.experimental_ns = false; + send_parameters.options.noise_suppression = params.noise_suppression; + send_parameters.options.auto_gain_control = params.auto_gain_control; + send_parameters.options.highpass_filter = false; + send_parameters.options.typing_detection = false; +// send_parameters.max_bandwidth_bps = 16000; + send_parameters.rtcp.reduced_size = true; + send_parameters.rtcp.remote_estimate = true; + voice_channel->SetSendParameters(send_parameters); +} + +void MediaEngineWebrtc::SetMute(bool mute) { + voice_channel->SetAudioSend(ssrc_send, !mute, nullptr, &audio_source); +} + +bool MediaEngineWebrtc::Sender::SendPacket(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) { + engine.Send(*packet); + rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent); + engine.OnSentPacket(sent_packet); + return true; +} + +bool MediaEngineWebrtc::Sender::SendRtcp(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) { + engine.Send(*packet); + rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent); + engine.OnSentPacket(sent_packet); + return true; +} + +int MediaEngineWebrtc::Sender::SetOption(cricket::MediaChannel::NetworkInterface::SocketType, rtc::Socket::Option, int) { + return -1; // in general, the result is not important yet +} + +MediaEngineWebrtc::Sender::Sender(MediaEngineWebrtc& engine) : engine(engine) {} + +MediaEngineWebrtc::AudioProcessor::AudioProcessor(webrtc::AudioTransport *transport_, + webrtc::TaskQueueFactory *task_queue_factory, MediaEngineBase& engine_, bool send_, bool recv_) + : send(send_) + , recv(recv_) + , transport(transport_) + , delay_us(frame_samples * 1000000 / clockrate) + , buf_send(nullptr) + , buf_recv(nullptr) + , engine(engine_) + , task_queue_send(std::make_unique(task_queue_factory->CreateTaskQueue( + "AudioProcessorSend", webrtc::TaskQueueFactory::Priority::NORMAL))) + , task_queue_recv(std::make_unique(task_queue_factory->CreateTaskQueue( + "AudioProcessorRecv", webrtc::TaskQueueFactory::Priority::NORMAL))) { + if (send) { + buf_send = new int16_t[frame_samples * channels]; + webrtc::RepeatingTaskHandle::Start(task_queue_send->Get(), [this]() { + static uint32_t new_mic_level = 0; + memset(buf_send, 0, frame_samples * channels * sample_bytes); + engine.Record(buf_send, frame_samples * channels); + transport->RecordedDataIsAvailable(buf_send, frame_samples, sample_bytes, channels, clockrate, + 0, 0, 0, false, new_mic_level); + return webrtc::TimeDelta::us(delay_us); + }); + } + if (recv) { + buf_recv = new int16_t[frame_samples * channels]; + webrtc::RepeatingTaskHandle::Start(task_queue_recv->Get(), [this]() { + static int64_t elapsed_time_ms = -1; + static int64_t ntp_time_ms = -1; + size_t samples_out = 0; + transport->NeedMorePlayData(frame_samples, sample_bytes, channels, clockrate, buf_recv, + samples_out, &elapsed_time_ms, &ntp_time_ms); + engine.Play(buf_recv, samples_out * channels); + return webrtc::TimeDelta::us(delay_us); + }); + } +} + +MediaEngineWebrtc::AudioProcessor::~AudioProcessor() { + task_queue_send = nullptr; + task_queue_recv = nullptr; + delete[] buf_send; + delete[] buf_recv; +} diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h new file mode 100644 index 0000000000..f6332d9f83 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h @@ -0,0 +1,78 @@ +#ifndef DEMO_MEDIAENGINEWEBRTC_H +#define DEMO_MEDIAENGINEWEBRTC_H + + +#include "MediaEngineBase.h" + +#include "api/transport/field_trial_based_config.h" +#include "call/call.h" +#include "media/base/media_engine.h" +#include "pc/rtp_sender.h" +#include "rtc_base/task_queue.h" + +#include + +class MediaEngineWebrtc : public MediaEngineBase { +public: + struct NetworkParams { + uint8_t min_bitrate_kbps; + uint8_t max_bitrate_kbps; + uint8_t start_bitrate_kbps; + uint8_t ptime_ms; + bool echo_cancellation; + bool auto_gain_control; + bool noise_suppression; + }; + + explicit MediaEngineWebrtc(bool outgoing, bool send = true, bool recv = true); + ~MediaEngineWebrtc() override; + void Receive(rtc::CopyOnWriteBuffer) override; + void OnSentPacket(const rtc::SentPacket& sent_packet); + void SetNetworkParams(const NetworkParams& params); + void SetMute(bool mute); + +private: + class Sender final : public cricket::MediaChannel::NetworkInterface { + public: + explicit Sender(MediaEngineWebrtc&); + bool SendPacket(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) override; + bool SendRtcp(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) override; + int SetOption(SocketType type, rtc::Socket::Option opt, int option) override; + private: + MediaEngineWebrtc& engine; + }; + + class AudioProcessor { + public: + AudioProcessor(webrtc::AudioTransport *transport, webrtc::TaskQueueFactory *task_queue_factory, + MediaEngineBase& engine, bool send, bool recv); + ~AudioProcessor(); + private: + bool send; + bool recv; + webrtc::AudioTransport *transport; + size_t delay_us; + int16_t *buf_send; + int16_t *buf_recv; + MediaEngineBase& engine; + std::unique_ptr task_queue_send; + std::unique_ptr task_queue_recv; + }; + + const uint32_t ssrc_send; + const uint32_t ssrc_recv; + std::unique_ptr call; + std::unique_ptr media_engine; + std::unique_ptr event_log; + std::unique_ptr task_queue_factory; + webrtc::FieldTrialBasedConfig field_trials; + webrtc::LocalAudioSinkAdapter audio_source; + Sender data_sender; + std::unique_ptr voice_channel; +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + std::unique_ptr audio_processor; +#endif +}; + + +#endif //DEMO_MEDIAENGINEWEBRTC_H diff --git a/submodules/TgVoipWebrtc/Impl/Message.h b/submodules/TgVoipWebrtc/Impl/Message.h new file mode 100644 index 0000000000..9d9f62e4ca --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Message.h @@ -0,0 +1,134 @@ +#ifndef DEMO_MESSAGE_H +#define DEMO_MESSAGE_H + +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/socket_address.h" + +namespace message { + +enum Type { + tUnknown, + tReady, + tConnected, + tDisconnected, + tRelayPing, + tRelayPong, + tGetPeerInfo, + tPeerInfo, + tSelfIPv6, + tSelfLocalIP, + tInit, + tInitAck, + tPing, + tPong, + tBufferOverflow, + tPacketIncorrect, + tWrongProtocol, + tRtpStream, +}; + +enum NetworkType { + nGprs, + nEdge, + n3gOrAbove, + nHighSpeed, + nUnknown, +}; + +struct Base { + virtual ~Base() = default; + explicit Base(Type ID) : ID(ID) {} + const Type ID; +}; + +struct Unknown : Base { + Unknown() : Base(Type::tUnknown) {} +}; + +struct Ready : Base { + Ready() : Base(Type::tReady) {} +}; + +struct Connected : Base { + Connected() : Base(Type::tConnected) {} +}; + +struct Disconnected : Base { + Disconnected() : Base(Type::tDisconnected) {} +}; + +struct RelayPing : Base { + RelayPing() : Base(Type::tRelayPing) {} +}; + +struct RelayPong : Base { + RelayPong() : Base(Type::tRelayPong) {} + uint32_t date{}; // int32_t in src + uint64_t query_id{}; //int64_t in src + rtc::SocketAddress my_addr; +}; + +struct GetPeerInfo : Base { + GetPeerInfo() : Base(Type::tGetPeerInfo) {} +}; + +struct PeerInfo : Base { + PeerInfo() : Base(Type::tPeerInfo) {} + rtc::SocketAddress my_addr; + rtc::SocketAddress peer_addr; +}; + +struct SelfIPv6 : Base { + SelfIPv6() : Base(Type::tSelfIPv6) {} + rtc::SocketAddress my_addr; +}; + +struct SelfLocalIP : Base { + SelfLocalIP() : Base(Type::tSelfLocalIP) {} +}; + +struct Init : Base { + Init() : Base(Type::tInit) {} + uint32_t ver{}; + uint32_t minVer{}; + uint32_t flags{}; +}; + +struct InitAck : Base { + InitAck() : Base(Type::tInitAck) {} + uint32_t ver{}; + uint32_t minVer{}; +}; + +struct Ping : Base { + Ping() : Base(Type::tPing) {} + uint32_t id{}; +}; + +struct Pong : Base { + Pong() : Base(Type::tPong) {} + uint32_t id{}; +}; + +struct BufferOverflow : Base { + BufferOverflow() : Base(Type::tBufferOverflow) {} +}; + +struct PacketIncorrect : Base { + PacketIncorrect() : Base(Type::tPacketIncorrect) {} +}; + +struct WrongProtocol : Base { + WrongProtocol() : Base(Type::tWrongProtocol) {} +}; + +struct RtpStream : Base { + RtpStream() : Base(Type::tRtpStream) {} + bool data_saving{false}; + NetworkType network_type{NetworkType::nUnknown}; + rtc::CopyOnWriteBuffer data; +}; + +} + +#endif //DEMO_MESSAGE_H diff --git a/submodules/TgVoipWebrtc/Impl/Protocol10.cpp b/submodules/TgVoipWebrtc/Impl/Protocol10.cpp new file mode 100644 index 0000000000..05d47da952 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Protocol10.cpp @@ -0,0 +1,159 @@ +#include "Protocol10.h" + +#include "rtc_base/byte_buffer.h" + +#include +#include + +const std::map Protocol10::decoders = { + {Protocol10::PacketType::tInit, Protocol10::InitDecode}, // back compatibility + {Protocol10::PacketType::tInitAck, Protocol10::InitAckDecode}, // back compatibility + {Protocol10::PacketType::tRtpStream, Protocol10::RtpStreamDecode}, + {Protocol10::PacketType::tPing, Protocol10::PingDecode}, + {Protocol10::PacketType::tPong, Protocol10::PongDecode}, +}; + +const std::map Protocol10::encoders = { + {message::tInit, Protocol10::InitEncode}, + {message::tInitAck, Protocol10::InitAckEncode}, + {message::tRtpStream, Protocol10::RtpStreamEncode}, + {message::tPing, Protocol10::PingEncode}, + {message::tPong, Protocol10::PongEncode}, +}; + +Protocol10::Protocol10() : ProtocolBase(10) {} + +std::unique_ptr Protocol10::ReadProtocolPacket(const uint8_t *buffer, size_t size) { + uint8_t type = buffer[0]; + auto deserializer = decoders.find(type); + if (deserializer == decoders.end()) + return nullptr; + return deserializer->second(buffer + 1, size - 1); +} + +rtc::Buffer Protocol10::WriteProtocolPacket(const message::Base *msg) { + auto serializer = encoders.find(msg->ID); + if (serializer == encoders.end()) + return rtc::Buffer(); + return serializer->second(msg); +} + +rtc::Buffer Protocol10::InitEncode(const message::Base *msg_base) { + const auto *msg = dynamic_cast(msg_base); + if (!msg) + return rtc::Buffer(); + rtc::ByteBufferWriter out; + out.WriteUInt8(PacketType::tInit); + out.Resize(14); + out.WriteUInt32(rtc::NetworkToHost32(msg->ver)); + out.WriteUInt32(rtc::NetworkToHost32(msg->minVer)); + out.WriteUInt32(rtc::NetworkToHost32(msg->flags)); + return rtc::Buffer(out.Data(), out.Length()); +} + +std::unique_ptr Protocol10::InitDecode(const uint8_t *buffer, size_t size) { + rtc::ByteBufferReader in(reinterpret_cast(buffer), size); + uint32_t ackId = 0, pseq = 0, acks = 0; + unsigned char pflags = 0; + in.ReadUInt32(&ackId); + in.ReadUInt32(&pseq); + in.ReadUInt32(&acks); + in.ReadUInt8(&pflags); + auto msg = std::make_unique(); + in.ReadUInt32(&msg->ver); + in.ReadUInt32(&msg->minVer); + in.ReadUInt32(&msg->flags); + msg->ver = rtc::HostToNetwork32(msg->ver); + msg->minVer = rtc::HostToNetwork32(msg->minVer); + msg->flags = rtc::HostToNetwork32(msg->flags); + if (ProtocolBase::IsSupported(msg->ver)) + return msg; + // TODO: support matching of lower supported versions + return std::make_unique(); +} + +rtc::Buffer Protocol10::InitAckEncode(const message::Base *msg_base) { + const auto *msg = dynamic_cast(msg_base); + if (!msg) + return rtc::Buffer(); + rtc::ByteBufferWriter out; + out.WriteUInt8(PacketType::tInitAck); + out.Resize(14); + out.WriteUInt32(rtc::NetworkToHost32(msg->ver)); + out.WriteUInt32(rtc::NetworkToHost32(msg->minVer)); + return rtc::Buffer(out.Data(), out.Length()); +} + +std::unique_ptr Protocol10::InitAckDecode(const uint8_t *buffer, size_t size) { + rtc::ByteBufferReader in(reinterpret_cast(buffer), size); + uint32_t ackId = 0, pseq = 0, acks = 0; + unsigned char pflags = 0; + in.ReadUInt32(&ackId); + in.ReadUInt32(&pseq); + in.ReadUInt32(&acks); + in.ReadUInt8(&pflags); + auto msg = std::make_unique(); + in.ReadUInt32(&msg->ver); + in.ReadUInt32(&msg->minVer); + msg->ver = rtc::HostToNetwork32(msg->ver); + msg->minVer = rtc::HostToNetwork32(msg->minVer); + if (ProtocolBase::IsSupported(msg->ver)) + return msg; + // TODO: support matching of lower supported versions + return std::make_unique(); +} + +rtc::Buffer Protocol10::RtpStreamEncode(const message::Base *msg_base) { + const auto *msg = dynamic_cast(msg_base); + if (!msg) + return rtc::Buffer(); + rtc::ByteBufferWriter out; + out.WriteUInt8(PacketType::tRtpStream); + uint8_t meta = (msg->network_type & 0b111) | (msg->data_saving << 3); + out.WriteUInt8(meta); + out.WriteBytes(reinterpret_cast(msg->data.data()), msg->data.size()); + return rtc::Buffer(out.Data(), out.Length()); +} + +std::unique_ptr Protocol10::RtpStreamDecode(const uint8_t *buffer, size_t size) { + auto msg = std::make_unique(); + uint8_t meta = buffer[0]; + msg->network_type = (message::NetworkType) (meta & 0b111); + msg->data_saving = (meta >> 3) & 0b1; + msg->data = rtc::CopyOnWriteBuffer(buffer + 1, size - 1); + return msg; +} + +rtc::Buffer Protocol10::PingEncode(const message::Base *msg_base) { + const auto *msg = dynamic_cast(msg_base); + if (!msg) + return rtc::Buffer(); + rtc::ByteBufferWriter out; + out.WriteUInt8(PacketType::tPing); + out.WriteUInt32(msg->id); + return rtc::Buffer(out.Data(), out.Length()); +} + +std::unique_ptr Protocol10::PingDecode(const uint8_t *buffer, size_t size) { + rtc::ByteBufferReader in(reinterpret_cast(buffer), size); + auto msg = std::make_unique(); + in.ReadUInt32(&msg->id); + return msg; +} + +rtc::Buffer Protocol10::PongEncode(const message::Base *msg_base) { + const auto *msg = dynamic_cast(msg_base); + if (!msg) + return rtc::Buffer(); + rtc::ByteBufferWriter out; + out.WriteUInt8(PacketType::tPong); + out.WriteUInt32(msg->id); + return rtc::Buffer(out.Data(), out.Length()); +} + +std::unique_ptr Protocol10::PongDecode(const uint8_t *buffer, size_t size) { + rtc::ByteBufferReader in(reinterpret_cast(buffer), size); + auto msg = std::make_unique(); + in.ReadUInt32(&msg->id); + return msg; +} diff --git a/submodules/TgVoipWebrtc/Impl/Protocol10.h b/submodules/TgVoipWebrtc/Impl/Protocol10.h new file mode 100644 index 0000000000..6ba7a6e4de --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/Protocol10.h @@ -0,0 +1,44 @@ +#ifndef DEMO_PROTOCOL10_H +#define DEMO_PROTOCOL10_H + + +#include "Message.h" +#include "ProtocolBase.h" + +#include + +class Protocol10 : public ProtocolBase { +public: + enum PacketType { + tInit = 1, + tInitAck, + tRtpStream, + tPing, + tPong, + }; + + Protocol10(); + std::unique_ptr ReadProtocolPacket(const uint8_t *buffer, size_t size) override; + rtc::Buffer WriteProtocolPacket(const message::Base *msg) override; + +private: + typedef std::function(const uint8_t *, size_t)> Deserializer; + typedef std::function Serializer; + + static const std::map decoders; + static const std::map encoders; + + static rtc::Buffer InitEncode(const message::Base *msg_base); + static std::unique_ptr InitDecode(const uint8_t *buffer, size_t size); + static rtc::Buffer InitAckEncode(const message::Base *msg_base); + static std::unique_ptr InitAckDecode(const uint8_t *buffer, size_t size); + static rtc::Buffer RtpStreamEncode(const message::Base *msg_base); + static std::unique_ptr RtpStreamDecode(const uint8_t *buffer, size_t size); + static rtc::Buffer PingEncode(const message::Base *msg_base); + static std::unique_ptr PingDecode(const uint8_t *buffer, size_t size); + static rtc::Buffer PongEncode(const message::Base *msg_base); + static std::unique_ptr PongDecode(const uint8_t *buffer, size_t size); +}; + + +#endif //DEMO_PROTOCOL10_H diff --git a/submodules/TgVoipWebrtc/Impl/ProtocolBase.cpp b/submodules/TgVoipWebrtc/Impl/ProtocolBase.cpp new file mode 100644 index 0000000000..38476d4d4b --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/ProtocolBase.cpp @@ -0,0 +1,23 @@ +#include "ProtocolBase.h" + +#include "Protocol10.h" + +const std::map ProtocolBase::constructors = { + {10, std::make_unique}, +}; + +const uint32_t ProtocolBase::actual_version = 10; +const uint32_t ProtocolBase::minimal_version = 10; + +std::unique_ptr ProtocolBase::CreateProtocol(uint32_t version) { + auto protocol = constructors.find(version); + if (protocol == constructors.end()) + return nullptr; + return protocol->second(); +} + +bool ProtocolBase::IsSupported(uint32_t version) { + return constructors.find(version) != constructors.end(); +} + +ProtocolBase::ProtocolBase(uint32_t version) : version(version) {} diff --git a/submodules/TgVoipWebrtc/Impl/ProtocolBase.h b/submodules/TgVoipWebrtc/Impl/ProtocolBase.h new file mode 100644 index 0000000000..e15610b0f6 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/ProtocolBase.h @@ -0,0 +1,33 @@ +#ifndef DEMO_PROTOCOLBASE_H +#define DEMO_PROTOCOLBASE_H + + +#include "Message.h" + +#include +#include +#include + +class ProtocolBase { +public: + static const uint32_t actual_version; + static const uint32_t minimal_version; + static std::unique_ptr CreateProtocol(uint32_t version); + static bool IsSupported(uint32_t version); + + virtual ~ProtocolBase() = default; + virtual std::unique_ptr ReadProtocolPacket(const uint8_t *buffer, size_t size) = 0; + virtual rtc::Buffer WriteProtocolPacket(const message::Base *msg) = 0; + + const uint32_t version; + +protected: + explicit ProtocolBase(uint32_t version); + +private: + typedef std::function()> Constructor; + static const std::map constructors; +}; + + +#endif //DEMO_PROTOCOLBASE_H diff --git a/submodules/TgVoipWebrtc/Impl/TgVoip.cpp b/submodules/TgVoipWebrtc/Impl/TgVoip.cpp new file mode 100644 index 0000000000..466fc5291a --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/TgVoip.cpp @@ -0,0 +1,406 @@ +#include + +#include "TgVoip.h" + +#include "Controller.h" +#include "Layer92.h" +#include "Message.h" + +#include +#include + +#ifndef TGVOIP_USE_CUSTOM_CRYPTO +extern "C" { +#include +#include +#include +#include +#include +} + +void tgvoip_openssl_aes_ige_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){ + AES_KEY akey; + AES_set_encrypt_key(key, 32*8, &akey); + AES_ige_encrypt(in, out, length, &akey, iv, AES_ENCRYPT); +} + +void tgvoip_openssl_aes_ige_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){ + AES_KEY akey; + AES_set_decrypt_key(key, 32*8, &akey); + AES_ige_encrypt(in, out, length, &akey, iv, AES_DECRYPT); +} + +void tgvoip_openssl_rand_bytes(uint8_t* buffer, size_t len){ + RAND_bytes(buffer, len); +} + +void tgvoip_openssl_sha1(uint8_t* msg, size_t len, uint8_t* output){ + SHA1(msg, len, output); +} + +void tgvoip_openssl_sha256(uint8_t* msg, size_t len, uint8_t* output){ + SHA256(msg, len, output); +} + +void tgvoip_openssl_aes_ctr_encrypt(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num){ + AES_KEY akey; + AES_set_encrypt_key(key, 32*8, &akey); + CRYPTO_ctr128_encrypt(inout, inout, length, &akey, iv, ecount, num, (block128_f) AES_encrypt); +} + +void tgvoip_openssl_aes_cbc_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){ + AES_KEY akey; + AES_set_encrypt_key(key, 256, &akey); + AES_cbc_encrypt(in, out, length, &akey, iv, AES_ENCRYPT); +} + +void tgvoip_openssl_aes_cbc_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){ + AES_KEY akey; + AES_set_decrypt_key(key, 256, &akey); + AES_cbc_encrypt(in, out, length, &akey, iv, AES_DECRYPT); +} + +const char * openssl_version() { + return SSLeay_version(SSLEAY_VERSION); +} + +CryptoFunctions Layer92::crypto={ + tgvoip_openssl_rand_bytes, + tgvoip_openssl_sha1, + tgvoip_openssl_sha256, + tgvoip_openssl_aes_ige_encrypt, + tgvoip_openssl_aes_ige_decrypt, + tgvoip_openssl_aes_ctr_encrypt, + tgvoip_openssl_aes_cbc_encrypt, + tgvoip_openssl_aes_cbc_decrypt +}; +#endif + + +#ifdef TGVOIP_NAMESPACE +namespace TGVOIP_NAMESPACE { +#endif + +class TgVoipImpl : public TgVoip, public sigslot::has_slots<> { +public: + TgVoipImpl( + std::vector const &endpoints, + TgVoipPersistentState const &persistentState, + std::unique_ptr const &proxy, + TgVoipConfig const &config, + TgVoipEncryptionKey const &encryptionKey, + TgVoipNetworkType initialNetworkType +#ifdef TGVOIP_USE_CUSTOM_CRYPTO + , + TgVoipCrypto const &crypto +#endif +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + , + TgVoipAudioDataCallbacks const &audioDataCallbacks +#endif + ) { +#ifdef TGVOIP_USE_CUSTOM_CRYPTO + tgvoip::VoIPController::crypto.sha1 = crypto.sha1; + tgvoip::VoIPController::crypto.sha256 = crypto.sha256; + tgvoip::VoIPController::crypto.rand_bytes = crypto.rand_bytes; + tgvoip::VoIPController::crypto.aes_ige_encrypt = crypto.aes_ige_encrypt; + tgvoip::VoIPController::crypto.aes_ige_decrypt = crypto.aes_ige_decrypt; + tgvoip::VoIPController::crypto.aes_ctr_encrypt = crypto.aes_ctr_encrypt; +#endif + +// std::cerr << "OpenSSL version: " << openssl_version() << std::endl; // to verify because of WebRTC BoringSSL + + EncryptionKey encryptionKeyValue; + memcpy(encryptionKeyValue, encryptionKey.value.data(), 256); + controller_ = new Controller(encryptionKey.isOutgoing, encryptionKeyValue, 5, 3); + +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + audioCallbacks = audioDataCallbacks; + controller_->SignalRecord.connect(this, &TgVoipImpl::record); + controller_->SignalPlay.connect(this, &TgVoipImpl::play); +#ifdef TGVOIP_PREPROCESSED_OUTPUT + controller_->SignalPreprocessed.connect(this, &TgVoipImpl::preprocessed); +#endif +#endif + + if (proxy != nullptr) { + controller_->SetProxy(rtc::ProxyType::PROXY_SOCKS5, rtc::SocketAddress(proxy->host, proxy->port), + proxy->login, proxy->password); + } + + controller_->SignalNewState.connect(this, &TgVoipImpl::controllerStateCallback); + controller_->Start(); + + for (const auto &endpoint : endpoints) { + rtc::SocketAddress addr(endpoint.host.ipv4, endpoint.port); + Controller::EndpointType type; + switch (endpoint.type) { + case TgVoipEndpointType::UdpRelay: + type = Controller::EndpointType::UDP; + break; + case TgVoipEndpointType::Lan: + case TgVoipEndpointType::Inet: + type = Controller::EndpointType::P2P; + break; + case TgVoipEndpointType::TcpRelay: + type = Controller::EndpointType::TCP; + break; + default: + type = Controller::EndpointType::UDP; + break; + } + controller_->AddEndpoint(addr, endpoint.peerTag, type); + } + + setNetworkType(initialNetworkType); + + switch (config.dataSaving) { + case TgVoipDataSaving::Mobile: + controller_->SetDataSaving(true); + break; + case TgVoipDataSaving::Always: + controller_->SetDataSaving(true); + break; + default: + controller_->SetDataSaving(false); + break; + } + } + + ~TgVoipImpl() override { + stop(); + } + + void setOnStateUpdated(std::function onStateUpdated) override { + std::lock_guard lock(m_onStateUpdated); + onStateUpdated_ = onStateUpdated; + } + + void setOnSignalBarsUpdated(std::function onSignalBarsUpdated) override { + std::lock_guard lock(m_onSignalBarsUpdated); + onSignalBarsUpdated_ = onSignalBarsUpdated; + } + + void setNetworkType(TgVoipNetworkType networkType) override { + message::NetworkType mappedType; + + switch (networkType) { + case TgVoipNetworkType::Unknown: + mappedType = message::NetworkType::nUnknown; + break; + case TgVoipNetworkType::Gprs: + mappedType = message::NetworkType::nGprs; + break; + case TgVoipNetworkType::Edge: + mappedType = message::NetworkType::nEdge; + break; + case TgVoipNetworkType::ThirdGeneration: + mappedType = message::NetworkType::n3gOrAbove; + break; + case TgVoipNetworkType::Hspa: + mappedType = message::NetworkType::n3gOrAbove; + break; + case TgVoipNetworkType::Lte: + mappedType = message::NetworkType::n3gOrAbove; + break; + case TgVoipNetworkType::WiFi: + mappedType = message::NetworkType::nHighSpeed; + break; + case TgVoipNetworkType::Ethernet: + mappedType = message::NetworkType::nHighSpeed; + break; + case TgVoipNetworkType::OtherHighSpeed: + mappedType = message::NetworkType::nHighSpeed; + break; + case TgVoipNetworkType::OtherLowSpeed: + mappedType = message::NetworkType::nEdge; + break; + case TgVoipNetworkType::OtherMobile: + mappedType = message::NetworkType::n3gOrAbove; + break; + case TgVoipNetworkType::Dialup: + mappedType = message::NetworkType::nGprs; + break; + default: + mappedType = message::NetworkType::nUnknown; + break; + } + + controller_->SetNetworkType(mappedType); + } + + void setMuteMicrophone(bool muteMicrophone) override { + controller_->SetMute(muteMicrophone); + } + + void setAudioOutputGainControlEnabled(bool enabled) override { + } + + void setEchoCancellationStrength(int strength) override { + } + + std::string getLastError() override { + return ""; // TODO: not implemented + } + + std::string getDebugInfo() override { + return ""; // TODO: not implemented + } + + int64_t getPreferredRelayId() override { + return 0; // we don't have endpoint ids + } + + TgVoipTrafficStats getTrafficStats() override { + return TgVoipTrafficStats{}; // TODO: not implemented + } + + TgVoipPersistentState getPersistentState() override { + return TgVoipPersistentState{}; // we dont't have such information + } + + TgVoipFinalState stop() override { + TgVoipFinalState finalState = { + }; + + delete controller_; + controller_ = nullptr; + + return finalState; + } + + void controllerStateCallback(Controller::State state) { + if (onStateUpdated_) { + TgVoipState mappedState; + switch (state) { + case Controller::State::WaitInit: + mappedState = TgVoipState::WaitInit; + break; + case Controller::State::WaitInitAck: + mappedState = TgVoipState::WaitInitAck; + break; + case Controller::State::Established: + mappedState = TgVoipState::Estabilished; + break; + case Controller::State::Failed: + mappedState = TgVoipState::Failed; + break; + case Controller::State::Reconnecting: + mappedState = TgVoipState::Reconnecting; + break; + default: + mappedState = TgVoipState::Estabilished; + break; + } + + onStateUpdated_(mappedState); + } + } + +private: +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + TgVoipAudioDataCallbacks audioCallbacks; + + void play(const int16_t *data, size_t size) { + if (!audioCallbacks.output) + return; + int16_t buf[size]; + memcpy(buf, data, size * 2); + audioCallbacks.output(buf, size); + } + + void record(int16_t *data, size_t size) { + if (audioCallbacks.input) + audioCallbacks.input(data, size); + } + + void preprocessed(const int16_t *data, size_t size) { + if (!audioCallbacks.preprocessed) + return; + int16_t buf[size]; + memcpy(buf, data, size * 2); + audioCallbacks.preprocessed(buf, size); + } +#endif + +private: + Controller *controller_; + std::function onStateUpdated_; + std::function onSignalBarsUpdated_; + std::mutex m_onStateUpdated, m_onSignalBarsUpdated; +}; + +std::function globalLoggingFunction; + +void __tgvoip_call_tglog(const char *format, ...){ + va_list vaArgs; + va_start(vaArgs, format); + + va_list vaCopy; + va_copy(vaCopy, vaArgs); + const int length = std::vsnprintf(nullptr, 0, format, vaCopy); + va_end(vaCopy); + + std::vector zc(length + 1); + std::vsnprintf(zc.data(), zc.size(), format, vaArgs); + va_end(vaArgs); + + if (globalLoggingFunction != nullptr) { + globalLoggingFunction(std::string(zc.data(), zc.size())); + } +} + +void TgVoip::setLoggingFunction(std::function loggingFunction) { + globalLoggingFunction = loggingFunction; +} + +void TgVoip::setGlobalServerConfig(const std::string &serverConfig) { +} + +int TgVoip::getConnectionMaxLayer() { + return 92; // TODO: retrieve from LayerBase +} + +std::string TgVoip::getVersion() { + return ""; // TODO: version not known while not released +} + +TgVoip *TgVoip::makeInstance( + TgVoipConfig const &config, + TgVoipPersistentState const &persistentState, + std::vector const &endpoints, + std::unique_ptr const &proxy, + TgVoipNetworkType initialNetworkType, + TgVoipEncryptionKey const &encryptionKey +#ifdef TGVOIP_USE_CUSTOM_CRYPTO +, + TgVoipCrypto const &crypto +#endif +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + , + TgVoipAudioDataCallbacks const &audioDataCallbacks +#endif +) { + return new TgVoipImpl( + endpoints, + persistentState, + proxy, + config, + encryptionKey, + initialNetworkType +#ifdef TGVOIP_USE_CUSTOM_CRYPTO + , + crypto +#endif +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + , + audioDataCallbacks +#endif + ); +} + +TgVoip::~TgVoip() = default; + +#ifdef TGVOIP_NAMESPACE +} +#endif diff --git a/submodules/TgVoipWebrtc/Impl/TgVoip.h b/submodules/TgVoipWebrtc/Impl/TgVoip.h new file mode 100644 index 0000000000..a0408040af --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/TgVoip.h @@ -0,0 +1,179 @@ +#ifndef __TGVOIP_H +#define __TGVOIP_H + +#define TGVOIP_NAMESPACE tgvoip_webrtc + +#include +#include +#include +#include + +#ifdef TGVOIP_NAMESPACE +namespace TGVOIP_NAMESPACE { +#endif + +struct TgVoipProxy { + std::string host; + uint16_t port; + std::string login; + std::string password; +}; + +enum class TgVoipEndpointType { + Inet, + Lan, + UdpRelay, + TcpRelay +}; + +struct TgVoipEdpointHost { + std::string ipv4; + std::string ipv6; +}; + +struct TgVoipEndpoint { + int64_t endpointId; + TgVoipEdpointHost host; + uint16_t port; + TgVoipEndpointType type; + unsigned char peerTag[16]; +}; + +enum class TgVoipNetworkType { + Unknown, + Gprs, + Edge, + ThirdGeneration, + Hspa, + Lte, + WiFi, + Ethernet, + OtherHighSpeed, + OtherLowSpeed, + OtherMobile, + Dialup +}; + +enum class TgVoipDataSaving { + Never, + Mobile, + Always +}; + +struct TgVoipPersistentState { + std::vector value; +}; + +#ifdef TGVOIP_USE_CUSTOM_CRYPTO +struct TgVoipCrypto { + void (*rand_bytes)(uint8_t* buffer, size_t length); + void (*sha1)(uint8_t* msg, size_t length, uint8_t* output); + void (*sha256)(uint8_t* msg, size_t length, uint8_t* output); + void (*aes_ige_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); + void (*aes_ige_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); + void (*aes_ctr_encrypt)(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num); + void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); + void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); +}; +#endif + +struct TgVoipConfig { + double initializationTimeout; + double receiveTimeout; + TgVoipDataSaving dataSaving; + bool enableP2P; + bool enableAEC; + bool enableNS; + bool enableAGC; + bool enableCallUpgrade; +#ifndef _WIN32 + std::string logPath; +#else + std::wstring logPath; +#endif + int maxApiLayer; +}; + +struct TgVoipEncryptionKey { + std::vector value; + bool isOutgoing; +}; + +enum class TgVoipState { + WaitInit, + WaitInitAck, + Estabilished, + Failed, + Reconnecting +}; + +struct TgVoipTrafficStats { + uint64_t bytesSentWifi; + uint64_t bytesReceivedWifi; + uint64_t bytesSentMobile; + uint64_t bytesReceivedMobile; +}; + +struct TgVoipFinalState { + TgVoipPersistentState persistentState; + std::string debugLog; + TgVoipTrafficStats trafficStats; + bool isRatingSuggested; +}; + +struct TgVoipAudioDataCallbacks { + std::function input; + std::function output; + std::function preprocessed; +}; + +class TgVoip { +protected: + TgVoip() = default; + +public: + static void setLoggingFunction(std::function loggingFunction); + static void setGlobalServerConfig(std::string const &serverConfig); + static int getConnectionMaxLayer(); + static std::string getVersion(); + static TgVoip *makeInstance( + TgVoipConfig const &config, + TgVoipPersistentState const &persistentState, + std::vector const &endpoints, + std::unique_ptr const &proxy, + TgVoipNetworkType initialNetworkType, + TgVoipEncryptionKey const &encryptionKey +#ifdef TGVOIP_USE_CUSTOM_CRYPTO + , + TgVoipCrypto const &crypto +#endif +#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO + , + TgVoipAudioDataCallbacks const &audioDataCallbacks +#endif + ); + + virtual ~TgVoip(); + + virtual void setNetworkType(TgVoipNetworkType networkType) = 0; + virtual void setMuteMicrophone(bool muteMicrophone) = 0; + virtual void setAudioOutputGainControlEnabled(bool enabled) = 0; + virtual void setEchoCancellationStrength(int strength) = 0; + + virtual std::string getLastError() = 0; + virtual std::string getDebugInfo() = 0; + virtual int64_t getPreferredRelayId() = 0; + virtual TgVoipTrafficStats getTrafficStats() = 0; + virtual TgVoipPersistentState getPersistentState() = 0; + + virtual void setOnStateUpdated(std::function onStateUpdated) = 0; + virtual void setOnSignalBarsUpdated(std::function onSignalBarsUpdated) = 0; + + virtual TgVoipFinalState stop() = 0; +}; + +#ifdef TGVOIP_NAMESPACE +} +#endif + +#endif diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h index d7f6e16846..15c752dfc0 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h @@ -3,7 +3,7 @@ #import -@interface OngoingCallConnectionDescription : NSObject +@interface OngoingCallConnectionDescriptionWebrtc : NSObject @property (nonatomic, readonly) int64_t connectionId; @property (nonatomic, strong, readonly) NSString * _Nonnull ip; @@ -15,14 +15,14 @@ @end -typedef NS_ENUM(int32_t, OngoingCallState) { +typedef NS_ENUM(int32_t, OngoingCallStateWebrtc) { OngoingCallStateInitializing, OngoingCallStateConnected, OngoingCallStateFailed, OngoingCallStateReconnecting }; -typedef NS_ENUM(int32_t, OngoingCallNetworkType) { +typedef NS_ENUM(int32_t, OngoingCallNetworkTypeWebrtc) { OngoingCallNetworkTypeWifi, OngoingCallNetworkTypeCellularGprs, OngoingCallNetworkTypeCellularEdge, @@ -30,20 +30,20 @@ typedef NS_ENUM(int32_t, OngoingCallNetworkType) { OngoingCallNetworkTypeCellularLte }; -typedef NS_ENUM(int32_t, OngoingCallDataSaving) { +typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) { OngoingCallDataSavingNever, OngoingCallDataSavingCellular, OngoingCallDataSavingAlways }; -@protocol OngoingCallThreadLocalContextQueue +@protocol OngoingCallThreadLocalContextQueueWebrtc - (void)dispatch:(void (^ _Nonnull)())f; - (bool)isCurrent; @end -@interface VoipProxyServer : NSObject +@interface VoipProxyServerWebrtc : NSObject @property (nonatomic, strong, readonly) NSString * _Nonnull host; @property (nonatomic, readonly) int32_t port; @@ -54,18 +54,18 @@ typedef NS_ENUM(int32_t, OngoingCallDataSaving) { @end -@interface OngoingCallThreadLocalContext : NSObject +@interface OngoingCallThreadLocalContextWebrtc : NSObject + (void)setupLoggingFunction:(void (* _Nullable)(NSString * _Nullable))loggingFunction; + (void)applyServerConfig:(NSString * _Nullable)data; + (int32_t)maxLayer; + (NSString * _Nonnull)version; -@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallState); +@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc); @property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t); -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath; -- (void)stop:(void (^_Nonnull)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion; +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath; +- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion; - (bool)needRate; @@ -74,7 +74,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSaving) { - (NSData * _Nonnull)getDerivedState; - (void)setIsMuted:(bool)isMuted; -- (void)setNetworkType:(OngoingCallNetworkType)networkType; +- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType; @end diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index e74531bdee..fb7571841b 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -1,44 +1,10 @@ -#import "OngoingCallThreadLocalContext.h" +#import "TgVoip/OngoingCallThreadLocalContext.h" #import "TgVoip.h" -#import -#include +using namespace TGVOIP_NAMESPACE; -static void TGCallAesIgeEncrypt(uint8_t *inBytes, uint8_t *outBytes, size_t length, uint8_t *key, uint8_t *iv) { - MTAesEncryptRaw(inBytes, outBytes, length, key, iv); -} - -static void TGCallAesIgeDecrypt(uint8_t *inBytes, uint8_t *outBytes, size_t length, uint8_t *key, uint8_t *iv) { - MTAesDecryptRaw(inBytes, outBytes, length, key, iv); -} - -static void TGCallSha1(uint8_t *msg, size_t length, uint8_t *output) { - MTRawSha1(msg, length, output); -} - -static void TGCallSha256(uint8_t *msg, size_t length, uint8_t *output) { - MTRawSha256(msg, length, output); -} - -static void TGCallAesCtrEncrypt(uint8_t *inOut, size_t length, uint8_t *key, uint8_t *iv, uint8_t *ecount, uint32_t *num) { - uint8_t *outData = (uint8_t *)malloc(length); - MTAesCtr *aesCtr = [[MTAesCtr alloc] initWithKey:key keyLength:32 iv:iv ecount:ecount num:*num]; - [aesCtr encryptIn:inOut out:outData len:length]; - memcpy(inOut, outData, length); - free(outData); - - [aesCtr getIv:iv]; - - memcpy(ecount, [aesCtr ecount], 16); - *num = [aesCtr num]; -} - -static void TGCallRandomBytes(uint8_t *buffer, size_t length) { - arc4random_buf(buffer, length); -} - -@implementation OngoingCallConnectionDescription +@implementation OngoingCallConnectionDescriptionWebrtc - (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag { self = [super init]; @@ -54,74 +20,11 @@ static void TGCallRandomBytes(uint8_t *buffer, size_t length) { @end -static MTAtomic *callContexts() { - static MTAtomic *instance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [[MTAtomic alloc] initWithValue:[[NSMutableDictionary alloc] init]]; - }); - return instance; -} - -@interface OngoingCallThreadLocalContextReference : NSObject - -@property (nonatomic, weak) OngoingCallThreadLocalContext *context; -@property (nonatomic, strong, readonly) id queue; - -@end - -@implementation OngoingCallThreadLocalContextReference - -- (instancetype)initWithContext:(OngoingCallThreadLocalContext *)context queue:(id)queue { - self = [super init]; - if (self != nil) { - self.context = context; - _queue = queue; - } - return self; -} - -@end - -static int32_t nextId = 1; - -static int32_t addContext(OngoingCallThreadLocalContext *context, id queue) { - int32_t contextId = OSAtomicIncrement32(&nextId); - [callContexts() with:^id(NSMutableDictionary *dict) { - dict[@(contextId)] = [[OngoingCallThreadLocalContextReference alloc] initWithContext:context queue:queue]; - return nil; - }]; - return contextId; -} - -static void removeContext(int32_t contextId) { - [callContexts() with:^id(NSMutableDictionary *dict) { - [dict removeObjectForKey:@(contextId)]; - return nil; - }]; -} - -static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalContext *)) { - __block OngoingCallThreadLocalContextReference *reference = nil; - [callContexts() with:^id(NSMutableDictionary *dict) { - reference = dict[@(contextId)]; - return nil; - }]; - if (reference != nil) { - [reference.queue dispatch:^{ - __strong OngoingCallThreadLocalContext *context = reference.context; - if (context != nil) { - f(context); - } - }]; - } -} - -@interface OngoingCallThreadLocalContext () { - id _queue; +@interface OngoingCallThreadLocalContextWebrtc () { + id _queue; int32_t _contextId; - OngoingCallNetworkType _networkType; + OngoingCallNetworkTypeWebrtc _networkType; NSTimeInterval _callReceiveTimeout; NSTimeInterval _callRingTimeout; NSTimeInterval _callConnectTimeout; @@ -129,7 +32,7 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte TgVoip *_tgVoip; - OngoingCallState _state; + OngoingCallStateWebrtc _state; int32_t _signalBars; NSData *_lastDerivedState; } @@ -139,7 +42,7 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte @end -@implementation VoipProxyServer +@implementation VoipProxyServerWebrtc - (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password { self = [super init]; @@ -154,7 +57,7 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte @end -static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkType type) { +static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkTypeWebrtc type) { switch (type) { case OngoingCallNetworkTypeWifi: return TgVoipNetworkType::WiFi; @@ -169,7 +72,7 @@ static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkType } } -static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSaving type) { +static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSavingWebrtc type) { switch (type) { case OngoingCallDataSavingNever: return TgVoipDataSaving::Never; @@ -182,7 +85,7 @@ static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSaving ty } } -@implementation OngoingCallThreadLocalContext +@implementation OngoingCallThreadLocalContextWebrtc static void (*InternalVoipLoggingFunction)(NSString *) = NULL; @@ -206,15 +109,14 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } + (NSString *)version { - return [NSString stringWithUTF8String:TgVoip::getVersion().c_str()]; + return @"2.7.7"; } -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath { +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath { self = [super init]; if (self != nil) { _queue = queue; assert([queue isCurrent]); - _contextId = addContext(self, queue); _callReceiveTimeout = 20.0; _callRingTimeout = 90.0; @@ -236,17 +138,17 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; proxyValue = std::unique_ptr(proxyObject); } - TgVoipCrypto crypto; + /*TgVoipCrypto crypto; crypto.sha1 = &TGCallSha1; crypto.sha256 = &TGCallSha256; crypto.rand_bytes = &TGCallRandomBytes; crypto.aes_ige_encrypt = &TGCallAesIgeEncrypt; crypto.aes_ige_decrypt = &TGCallAesIgeDecrypt; - crypto.aes_ctr_encrypt = &TGCallAesCtrEncrypt; + crypto.aes_ctr_encrypt = &TGCallAesCtrEncrypt;*/ std::vector endpoints; - NSArray *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections]; - for (OngoingCallConnectionDescription *connection in connections) { + NSArray *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections]; + for (OngoingCallConnectionDescriptionWebrtc *connection in connections) { unsigned char peerTag[16]; [connection.peerTag getBytes:peerTag length:16]; @@ -272,7 +174,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; .enableAGC = true, .enableCallUpgrade = false, .logPath = logPath.length == 0 ? "" : std::string(logPath.UTF8String), - .maxApiLayer = [OngoingCallThreadLocalContext maxLayer] + .maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer] }; std::vector encryptionKeyValue; @@ -284,40 +186,27 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; .isOutgoing = isOutgoing, }; - /* - TgVoipConfig const &config, - TgVoipPersistentState const &persistentState, - std::vector const &endpoints, - std::unique_ptr const &proxy, - TgVoipNetworkType initialNetworkType, - TgVoipEncryptionKey const &encryptionKey - #ifdef TGVOIP_USE_CUSTOM_CRYPTO - , - TgVoipCrypto const &crypto - */ - _tgVoip = TgVoip::makeInstance( config, { derivedStateValue }, endpoints, proxyValue, callControllerNetworkTypeForType(networkType), - encryptionKey, - crypto + encryptionKey ); _state = OngoingCallStateInitializing; _signalBars = -1; - __weak OngoingCallThreadLocalContext *weakSelf = self; + __weak OngoingCallThreadLocalContextWebrtc *weakSelf = self; _tgVoip->setOnStateUpdated([weakSelf](TgVoipState state) { - __strong OngoingCallThreadLocalContext *strongSelf = weakSelf; + __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf; if (strongSelf) { [strongSelf controllerStateChanged:state]; } }); _tgVoip->setOnSignalBarsUpdated([weakSelf](int signalBars) { - __strong OngoingCallThreadLocalContext *strongSelf = weakSelf; + __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf; if (strongSelf) { [strongSelf signalBarsChanged:signalBars]; } @@ -328,12 +217,15 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; - (void)dealloc { assert([_queue isCurrent]); - removeContext(_contextId); if (_tgVoip != NULL) { [self stop:nil]; } } +- (bool)needRate { + return false; +} + - (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion { if (_tgVoip) { TgVoipFinalState finalState = _tgVoip->stop(); @@ -352,8 +244,10 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; - (NSString *)debugInfo { if (_tgVoip != nil) { - auto rawDebugString = _tgVoip->getDebugInfo(); - return [NSString stringWithUTF8String:rawDebugString.c_str()]; + NSString *version = [self version]; + return [NSString stringWithFormat:@"WebRTC, Version: %@", version]; + //auto rawDebugString = _tgVoip->getDebugInfo(); + //return [NSString stringWithUTF8String:rawDebugString.c_str()]; } else { return nil; } @@ -361,7 +255,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; - (NSString *)version { if (_tgVoip != nil) { - return [NSString stringWithUTF8String:_tgVoip->getVersion().c_str()]; + return @"2.7.7";//[NSString stringWithUTF8String:_tgVoip->getVersion().c_str()]; } else { return nil; } @@ -379,7 +273,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } - (void)controllerStateChanged:(TgVoipState)state { - OngoingCallState callState = OngoingCallStateInitializing; + OngoingCallStateWebrtc callState = OngoingCallStateInitializing; switch (state) { case TgVoipState::Estabilished: callState = OngoingCallStateConnected; @@ -419,7 +313,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } } -- (void)setNetworkType:(OngoingCallNetworkType)networkType { +- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType { if (_networkType != networkType) { _networkType = networkType; if (_tgVoip) { @@ -429,3 +323,4 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } @end + diff --git a/submodules/WalletUI/BUILD b/submodules/WalletUI/BUILD index a5159bc357..6c42bedab0 100644 --- a/submodules/WalletUI/BUILD +++ b/submodules/WalletUI/BUILD @@ -39,8 +39,6 @@ swift_library( "//submodules/UrlEscaping:UrlEscaping", "//submodules/LocalAuth:LocalAuth", "//submodules/ScreenCaptureDetection:ScreenCaptureDetection", - "//submodules/WalletUrl:WalletUrl", - "//submodules/WalletCore:WalletCore", "//submodules/ActivityIndicator:ActivityIndicator", "//submodules/ProgressNavigationButtonNode:ProgressNavigationButtonNode", "//submodules/Markdown:Markdown", diff --git a/submodules/openssl/BUILD b/submodules/openssl/BUILD index 955faeca8d..2770f12648 100644 --- a/submodules/openssl/BUILD +++ b/submodules/openssl/BUILD @@ -107,6 +107,7 @@ openssl_headers = [ openssl_libs = [ "libcrypto.a", + "libssl.a", ] genrule( @@ -119,6 +120,7 @@ genrule( ], cmd_bash = """ + if [ "$(TARGET_CPU)" == "ios_armv7" ]; then BUILD_ARCH="armv7" elif [ "$(TARGET_CPU)" == "ios_arm64" ]; then diff --git a/third-party/BUILD b/third-party/BUILD index e69de29bb2..44e0b20555 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -0,0 +1,8 @@ + +filegroup( + name = "depot_tools_sources", + srcs = glob([ + "depot_tools/**/*" + ]), + visibility = ["//visibility:public"], +) diff --git a/third-party/depot_tools b/third-party/depot_tools new file mode 160000 index 0000000000..ae7f4c5111 --- /dev/null +++ b/third-party/depot_tools @@ -0,0 +1 @@ +Subproject commit ae7f4c51114152e95cf1e9b6d2f0a4936fb7c5d6 diff --git a/third-party/webrtc/BUILD b/third-party/webrtc/BUILD new file mode 100644 index 0000000000..fd2b2e8807 --- /dev/null +++ b/third-party/webrtc/BUILD @@ -0,0 +1,78 @@ +webrtc_libs = [ + "libwebrtc.a", +] + +filegroup( + name = "webrtc_sources", + srcs = glob([ + "webrtc-ios/**/*" + ]), +) + +genrule( + name = "webrtc_build", + srcs = [ + "build-webrtc-bazel.sh", + "patch.sh", + ":webrtc_sources", + "//third-party:depot_tools_sources", + "//submodules/openssl:openssl_include", + "//submodules/openssl:libcrypto.a", + "//submodules/openssl:libssl.a", + ], + cmd_bash = + """ + OUT_DIR="ios" + if [ "$(TARGET_CPU)" == "ios_armv7" ]; then + BUILD_ARCH="armv7" + elif [ "$(TARGET_CPU)" == "ios_arm64" ]; then + BUILD_ARCH="arm64" + elif [ "$(TARGET_CPU)" == "ios_x86_64" ]; then + BUILD_ARCH="x64" + OUT_DIR="ios_sim" + else + echo "Unsupported architecture $(TARGET_CPU)" + fi + BUILD_DIR="$(RULEDIR)/$$BUILD_ARCH" + rm -rf "$$BUILD_DIR" + mkdir -p "$$BUILD_DIR" + + SOURCE_PATH="third-party/webrtc/webrtc-ios/src" + + rsync -aqW "$$SOURCE_PATH" "$$BUILD_DIR/" + #cp -R "$$SOURCE_PATH" "$$BUILD_DIR/" + + DEPOT_TOOLS_PATH="third-party/depot_tools" + + rm -rf "$$BUILD_DIR/depot_tools" + cp -R "$$DEPOT_TOOLS_PATH" "$$BUILD_DIR/" + + rm -rf "$$BUILD_DIR/openssl" + mkdir -p "$$BUILD_DIR/openssl/include/openssl" + for f in $(locations //submodules/openssl:openssl_include); do + cp -f "$$f" "$$BUILD_DIR/openssl/include/openssl/" + done + + mkdir -p "$$BUILD_DIR/openssl/lib" + cp -f "$(location //submodules/openssl:libcrypto.a)" "$$BUILD_DIR/openssl/" + cp -f "$(location //submodules/openssl:libssl.a)" "$$BUILD_DIR/openssl/" + + rm -f "$$BUILD_DIR/build-webrtc-bazel.sh" + cp $(location build-webrtc-bazel.sh) "$$BUILD_DIR/" + + rm -f "$$BUILD_DIR/patch.sh" + cp $(location patch.sh) "$$BUILD_DIR/" + + sh $$BUILD_DIR/build-webrtc-bazel.sh "$$BUILD_DIR" $$BUILD_ARCH + """ + "\n".join([ + "cp -f $$BUILD_DIR/src/out/$$OUT_DIR/obj/{lib} $(location {lib})".format(lib=lib) for lib in webrtc_libs + ]), + outs = webrtc_libs, + visibility = ["//visibility:public",] +) + +cc_library( + name = "webrtc_lib", + srcs = [":" + x for x in webrtc_libs], + visibility = ["//visibility:public"], +) diff --git a/third-party/webrtc/build-webrtc-bazel.sh b/third-party/webrtc/build-webrtc-bazel.sh new file mode 100755 index 0000000000..bad6eb9722 --- /dev/null +++ b/third-party/webrtc/build-webrtc-bazel.sh @@ -0,0 +1,29 @@ +#/bin/sh + +set -x +set -e + +BUILD_DIR="$(pwd)/$1" +ARCH="$2" + +echo "BUILD_DIR=$BUILD_DIR" +echo "ARCH=$ARCH" + +export PATH="$PATH:$BUILD_DIR/depot_tools" + +rm -rf "$BUILD_DIR/src/openssl" +cp -R "$BUILD_DIR/openssl" "$BUILD_DIR/src/" + +pushd "$BUILD_DIR/src" + +sh "../patch.sh" || true + +OUT_DIR="ios" +if [ "$ARCH" == "x64" ]; then + OUT_DIR="ios_sim" +fi + +gn gen out/$OUT_DIR --args="use_xcode_clang=true "" target_cpu=\"$ARCH\""' target_os="ios" is_debug=false is_component_build=false rtc_include_tests=false use_rtti=true rtc_use_x11=false use_custom_libcxx=false use_custom_libcxx_for_host=false rtc_include_builtin_video_codecs=false rtc_build_ssl=false rtc_build_examples=false rtc_build_tools=false ios_deployment_target="9.0" ios_enable_code_signing=false is_unsafe_developer_build=false rtc_enable_protobuf=false rtc_include_builtin_video_codecs=false rtc_use_gtk=false rtc_use_metal_rendering=false rtc_ssl_root="//openssl"' +ninja -C out/$OUT_DIR webrtc + +popd diff --git a/third-party/webrtc/patch.sh b/third-party/webrtc/patch.sh new file mode 100644 index 0000000000..8b6e418e81 --- /dev/null +++ b/third-party/webrtc/patch.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +PATCH=$(cat <<-END +--- a/rtc_base/BUILD.gn ++++ b/rtc_base/BUILD.gn +@@ -23,7 +23,11 @@ if (!rtc_build_ssl) { + config("external_ssl_library") { + assert(rtc_ssl_root != "", + "You must specify rtc_ssl_root when rtc_build_ssl==0.") +- include_dirs = [ rtc_ssl_root ] ++ include_dirs = [ "\$rtc_ssl_root/include" ] ++ libs = [ ++ "\$rtc_ssl_root/libssl.a", ++ "\$rtc_ssl_root/libcrypto.a" ++ ] + } + } + +--- a/third_party/usrsctp/BUILD.gn ++++ b/third_party/usrsctp/BUILD.gn +@@ -3,6 +3,7 @@ + # found in the LICENSE file. + + import("//build/toolchain/toolchain.gni") ++import("//webrtc.gni") + + config("usrsctp_config") { + include_dirs = [ +@@ -140,7 +141,9 @@ static_library("usrsctp") { + if (is_fuchsia) { + defines += [ "__Userspace_os_Fuchsia" ] + } +- deps = [ +- "//third_party/boringssl", +- ] ++ if (rtc_build_ssl) { ++ deps += [ "//third_party/boringssl" ] ++ } else { ++ configs += [ "//rtc_base:external_ssl_library" ] ++ } + } + +--- a/third_party/libsrtp/BUILD.gn ++++ b/third_party/libsrtp/BUILD.gn +@@ -3,6 +3,7 @@ + # found in the LICENSE file. + + import("//testing/test.gni") ++import("//webrtc.gni") + + declare_args() { + # Tests may not be appropriate for some build environments, e.g. Windows. +@@ -114,9 +115,11 @@ static_library("libsrtp") { + "srtp/ekt.c", + "srtp/srtp.c", + ] +- public_deps = [ +- "//third_party/boringssl:boringssl", +- ] ++ if (rtc_build_ssl) { ++ public_deps = [ "//third_party/boringssl" ] ++ } else { ++ configs += [ "//rtc_base:external_ssl_library" ] ++ } + } + + if (build_libsrtp_tests) { +END +) + +echo "$PATCH" | patch -p1 diff --git a/third-party/webrtc/webrtc-ios b/third-party/webrtc/webrtc-ios new file mode 160000 index 0000000000..6774acc02f --- /dev/null +++ b/third-party/webrtc/webrtc-ios @@ -0,0 +1 @@ +Subproject commit 6774acc02feb905ee8209a2e187c8cc464c87289