diff --git a/.bazelrc b/.bazelrc index 1ffa5c2f15..55d4d3adf2 100644 --- a/.bazelrc +++ b/.bazelrc @@ -4,9 +4,6 @@ build --action_env=ZERO_AR_DATE=1 build --strategy=Genrule=local build --apple_platform_type=ios build --cxxopt='-std=c++14' -build --copt='-w' -build --swiftcopt='-Xcc' -build --swiftcopt='-w' build --spawn_strategy=local build --strategy=SwiftCompile=local build --features=debug_prefix_map_pwd_is_dot diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index 919d0fd189..7ccec75fc1 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -167,9 +167,16 @@ public final class PresentationCallImpl: PresentationCall { public let isOutgoing: Bool public let peer: Peer? + private let serializedData: String? + private let dataSaving: VoiceCallDataSaving + private let derivedState: VoipDerivedState + private let proxyServer: ProxyServerSettings? + private let currentNetworkType: NetworkType + private let updatedNetworkType: Signal + private var sessionState: CallSession? private var callContextState: OngoingCallContextState? - private var ongoingContext: OngoingCallContext + private var ongoingContext: OngoingCallContext? private var ongoingContextStateDisposable: Disposable? private var reception: Int32? private var receptionDisposable: Disposable? @@ -198,6 +205,8 @@ public final class PresentationCallImpl: PresentationCall { return self.audioOutputStatePromise.get() } + private let debugInfoValue = Promise<(String, String)>(("", "")) + private let canBeRemovedPromise = Promise(false) private var didSetCanBeRemoved = false public var canBeRemoved: Signal { @@ -233,7 +242,12 @@ public final class PresentationCallImpl: PresentationCall { self.isOutgoing = isOutgoing self.peer = peer - self.ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: currentNetworkType, updatedNetworkType: updatedNetworkType, serializedData: serializedData, dataSaving: dataSaving, derivedState: derivedState) + self.serializedData = serializedData + self.dataSaving = dataSaving + self.derivedState = derivedState + self.proxyServer = proxyServer + self.currentNetworkType = currentNetworkType + self.updatedNetworkType = updatedNetworkType var didReceiveAudioOutputs = false @@ -251,28 +265,6 @@ public final class PresentationCallImpl: PresentationCall { } }) - self.ongoingContextStateDisposable = (self.ongoingContext.state - |> deliverOnMainQueue).start(next: { [weak self] contextState in - if let strongSelf = self { - if let sessionState = strongSelf.sessionState { - strongSelf.updateSessionState(sessionState: sessionState, callContextState: contextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl) - } else { - strongSelf.callContextState = contextState - } - } - }) - - self.receptionDisposable = (self.ongoingContext.reception - |> deliverOnMainQueue).start(next: { [weak self] reception in - if let strongSelf = self { - if let sessionState = strongSelf.sessionState { - strongSelf.updateSessionState(sessionState: sessionState, callContextState: strongSelf.callContextState, reception: reception, audioSessionControl: strongSelf.audioSessionControl) - } else { - strongSelf.reception = reception - } - } - }) - self.audioSessionDisposable = audioSession.push(audioSessionType: .voiceCall, manualActivate: { [weak self] control in Queue.mainQueue().async { if let strongSelf = self { @@ -485,7 +477,34 @@ public final class PresentationCallImpl: PresentationCall { self.audioSessionShouldBeActive.set(true) if let _ = audioSessionControl, !wasActive || previousControl == nil { let logName = "\(id.id)_\(id.accessHash)" - self.ongoingContext.start(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, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName) + self.ongoingContext = ongoingContext + + self.debugInfoValue.set(ongoingContext.debugInfo()) + + self.ongoingContextStateDisposable = (ongoingContext.state + |> deliverOnMainQueue).start(next: { [weak self] contextState in + if let strongSelf = self { + if let sessionState = strongSelf.sessionState { + strongSelf.updateSessionState(sessionState: sessionState, callContextState: contextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl) + } else { + strongSelf.callContextState = contextState + } + } + }) + + self.receptionDisposable = (ongoingContext.reception + |> deliverOnMainQueue).start(next: { [weak self] reception in + if let strongSelf = self { + if let sessionState = strongSelf.sessionState { + strongSelf.updateSessionState(sessionState: sessionState, callContextState: strongSelf.callContextState, reception: reception, audioSessionControl: strongSelf.audioSessionControl) + } else { + strongSelf.reception = reception + } + } + }) + if sessionState.isOutgoing { self.callKitIntegration?.reportOutgoingCallConnected(uuid: sessionState.id, at: Date()) } @@ -494,13 +513,13 @@ public final class PresentationCallImpl: PresentationCall { self.audioSessionShouldBeActive.set(true) if wasActive { let debugLogValue = Promise() - self.ongoingContext.stop(callId: id, sendDebugLogs: options.contains(.sendDebugLogs), debugLogValue: debugLogValue) + self.ongoingContext?.stop(callId: id, sendDebugLogs: options.contains(.sendDebugLogs), debugLogValue: debugLogValue) } default: self.audioSessionShouldBeActive.set(false) if wasActive { let debugLogValue = Promise() - self.ongoingContext.stop(debugLogValue: debugLogValue) + self.ongoingContext?.stop(debugLogValue: debugLogValue) } } if case .terminated = sessionState.state, !wasTerminated { @@ -531,12 +550,6 @@ public final class PresentationCallImpl: PresentationCall { self.statePromise.set(presentationState) self.updateTone(presentationState, callContextState: callContextState, previous: previous) } - - if !self.shouldPresentCallRating { - self.ongoingContext.needsRating { needsRating in - self.shouldPresentCallRating = needsRating - } - } } private func updateTone(_ state: PresentationCallState, callContextState: OngoingCallContextState?, previous: CallSession?) { @@ -617,7 +630,7 @@ public final class PresentationCallImpl: PresentationCall { public func hangUp() -> Signal { let debugLogValue = Promise() self.callSessionManager.drop(internalId: self.internalId, reason: .hangUp, debugLog: debugLogValue.get()) - self.ongoingContext.stop(debugLogValue: debugLogValue) + self.ongoingContext?.stop(debugLogValue: debugLogValue) return self.hungUpPromise.get() } @@ -625,7 +638,7 @@ public final class PresentationCallImpl: PresentationCall { public func rejectBusy() { self.callSessionManager.drop(internalId: self.internalId, reason: .busy, debugLog: .single(nil)) let debugLog = Promise() - self.ongoingContext.stop(debugLogValue: debugLog) + self.ongoingContext?.stop(debugLogValue: debugLog) } public func toggleIsMuted() { @@ -635,7 +648,7 @@ public final class PresentationCallImpl: PresentationCall { public func setIsMuted(_ value: Bool) { self.isMutedValue = value self.isMutedPromise.set(self.isMutedValue) - self.ongoingContext.setIsMuted(self.isMutedValue) + self.ongoingContext?.setIsMuted(self.isMutedValue) } public func setCurrentAudioOutput(_ output: AudioSessionOutput) { @@ -656,6 +669,6 @@ public final class PresentationCallImpl: PresentationCall { } public func debugInfo() -> Signal<(String, String), NoError> { - return self.ongoingContext.debugInfo() + return self.debugInfoValue.get() } } diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 876978cd37..825d18935c 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -172,12 +172,12 @@ public final class OngoingCallContext { public static var maxLayer: Int32 { return OngoingCallThreadLocalContext.maxLayer() } - + public static var version: String { - return OngoingCallThreadLocalContext.version()! + return OngoingCallThreadLocalContext.version() } - - public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState) { + + 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) { let _ = setupLogs OngoingCallThreadLocalContext.applyServerConfig(serializedData) @@ -186,34 +186,42 @@ public final class OngoingCallContext { self.callSessionManager = callSessionManager let queue = self.queue - self.queue.async { - var voipProxyServer: VoipProxyServer? - if let proxyServer = proxyServer { - switch proxyServer.connection { + + cleanupCallLogs(account: account) + + let logPath = logName.isEmpty ? "" : callLogsPath(account: self.account) + "/" + logName + ".log" + self.audioSessionDisposable.set((audioSessionActive + |> filter { $0 } + |> 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 + } } + 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 = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: derivedState.data) - self.contextRef = Unmanaged.passRetained(context) - context.stateChanged = { [weak self] state in - self?.contextState.set(.single(state)) - } - context.signalBarsChanged = { [weak self] signalBars in - self?.receptionPromise.set(.single(signalBars)) - } - } - - self.networkTypeDisposable = (updatedNetworkType - |> deliverOn(self.queue)).start(next: { [weak self] networkType in - self?.withContext { context in - context.setNetworkType(ongoingNetworkTypeForType(networkType)) - } - }) - - cleanupCallLogs(account: account) + })) } deinit { @@ -235,19 +243,6 @@ public final class OngoingCallContext { } } - public func start(key: Data, isOutgoing: Bool, connections: CallSessionConnectionSet, maxLayer: Int32, allowP2P: Bool, audioSessionActive: Signal, logName: String) { - let logPath = logName.isEmpty ? "" : callLogsPath(account: self.account) + "/" + logName + ".log" - self.audioSessionDisposable.set((audioSessionActive - |> filter { $0 } - |> take(1)).start(next: { [weak self] _ in - if let strongSelf = self { - strongSelf.withContext { context in - context.start(withKey: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath) - } - } - })) - } - public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise) { self.withContext { context in context.stop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in @@ -293,14 +288,5 @@ public final class OngoingCallContext { } return (poll |> then(.complete() |> delay(0.5, queue: Queue.concurrentDefaultQueue()))) |> restart } - - public func needsRating(_ completion: @escaping (Bool) -> Void) { - self.withContext { context in - let needsRating = context.needRate() - Queue.mainQueue().async { - completion(needsRating) - } - } - } } diff --git a/submodules/TgVoip/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoip/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h index 674a80d80e..d7f6e16846 100644 --- a/submodules/TgVoip/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoip/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h @@ -59,14 +59,13 @@ typedef NS_ENUM(int32_t, OngoingCallDataSaving) { + (void)setupLoggingFunction:(void (* _Nullable)(NSString * _Nullable))loggingFunction; + (void)applyServerConfig:(NSString * _Nullable)data; + (int32_t)maxLayer; -+ (NSString *)version; ++ (NSString * _Nonnull)version; @property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallState); @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; -- (void)startWithKey:(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 (^)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion; +- (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; - (bool)needRate; diff --git a/submodules/TgVoip/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoip/Sources/OngoingCallThreadLocalContext.mm index 4da89550f7..e74531bdee 100644 --- a/submodules/TgVoip/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoip/Sources/OngoingCallThreadLocalContext.mm @@ -1,10 +1,9 @@ #import "OngoingCallThreadLocalContext.h" -#import "VoIPController.h" -#import "VoIPServerConfig.h" -#import "os/darwin/SetupLogging.h" +#import "TgVoip.h" #import +#include static void TGCallAesIgeEncrypt(uint8_t *inBytes, uint8_t *outBytes, size_t length, uint8_t *key, uint8_t *iv) { MTAesEncryptRaw(inBytes, outBytes, length, key, iv); @@ -127,34 +126,19 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte NSTimeInterval _callRingTimeout; NSTimeInterval _callConnectTimeout; NSTimeInterval _callPacketTimeout; - int32_t _dataSavingMode; - tgvoip::VoIPController *_controller; + TgVoip *_tgVoip; OngoingCallState _state; int32_t _signalBars; NSData *_lastDerivedState; } -- (void)controllerStateChanged:(int)state; +- (void)controllerStateChanged:(TgVoipState)state; - (void)signalBarsChanged:(int32_t)signalBars; @end -static void controllerStateCallback(tgvoip::VoIPController *controller, int state) { - int32_t contextId = (int32_t)((intptr_t)controller->implData); - withContext(contextId, ^(OngoingCallThreadLocalContext *context) { - [context controllerStateChanged:state]; - }); -} - -static void signalBarsCallback(tgvoip::VoIPController *controller, int signalBars) { - int32_t contextId = (int32_t)((intptr_t)controller->implData); - withContext(contextId, ^(OngoingCallThreadLocalContext *context) { - [context signalBarsChanged:(int32_t)signalBars]; - }); -} - @implementation VoipProxyServer - (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password { @@ -170,55 +154,62 @@ static void signalBarsCallback(tgvoip::VoIPController *controller, int signalBar @end -static int callControllerNetworkTypeForType(OngoingCallNetworkType type) { +static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkType type) { switch (type) { - case OngoingCallNetworkTypeWifi: - return tgvoip::NET_TYPE_WIFI; - case OngoingCallNetworkTypeCellularGprs: - return tgvoip::NET_TYPE_GPRS; - case OngoingCallNetworkTypeCellular3g: - return tgvoip::NET_TYPE_3G; - case OngoingCallNetworkTypeCellularLte: - return tgvoip::NET_TYPE_LTE; - default: - return tgvoip::NET_TYPE_WIFI; + case OngoingCallNetworkTypeWifi: + return TgVoipNetworkType::WiFi; + case OngoingCallNetworkTypeCellularGprs: + return TgVoipNetworkType::Gprs; + case OngoingCallNetworkTypeCellular3g: + return TgVoipNetworkType::ThirdGeneration; + case OngoingCallNetworkTypeCellularLte: + return TgVoipNetworkType::Lte; + default: + return TgVoipNetworkType::ThirdGeneration; } } -static int callControllerDataSavingForType(OngoingCallDataSaving type) { +static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSaving type) { switch (type) { - case OngoingCallDataSavingNever: - return tgvoip::DATA_SAVING_NEVER; - case OngoingCallDataSavingCellular: - return tgvoip::DATA_SAVING_MOBILE; - case OngoingCallDataSavingAlways: - return tgvoip::DATA_SAVING_ALWAYS; - default: - return tgvoip::DATA_SAVING_NEVER; + case OngoingCallDataSavingNever: + return TgVoipDataSaving::Never; + case OngoingCallDataSavingCellular: + return TgVoipDataSaving::Mobile; + case OngoingCallDataSavingAlways: + return TgVoipDataSaving::Always; + default: + return TgVoipDataSaving::Never; } } @implementation OngoingCallThreadLocalContext +static void (*InternalVoipLoggingFunction)(NSString *) = NULL; + + (void)setupLoggingFunction:(void (*)(NSString *))loggingFunction { - TGVoipLoggingFunction = loggingFunction; + InternalVoipLoggingFunction = loggingFunction; + TgVoip::setLoggingFunction([](std::string const &string) { + if (InternalVoipLoggingFunction) { + InternalVoipLoggingFunction([[NSString alloc] initWithUTF8String:string.c_str()]); + } + }); } + (void)applyServerConfig:(NSString *)string { if (string.length != 0) { - tgvoip::ServerConfig::GetSharedInstance()->Update(std::string(string.UTF8String)); + TgVoip::setGlobalServerConfig(std::string(string.UTF8String)); } } + (int32_t)maxLayer { - return tgvoip::VoIPController::GetConnectionMaxLayer(); + return 92; } + (NSString *)version { - return [NSString stringWithUTF8String:tgvoip::VoIPController::GetVersion()]; + return [NSString stringWithUTF8String:TgVoip::getVersion().c_str()]; } -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving derivedState:(NSData * _Nonnull)derivedState { +- (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 { self = [super init]; if (self != nil) { _queue = queue; @@ -229,37 +220,108 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { _callRingTimeout = 90.0; _callConnectTimeout = 30.0; _callPacketTimeout = 10.0; - _dataSavingMode = callControllerDataSavingForType(dataSaving); _networkType = networkType; - _controller = new tgvoip::VoIPController(); - _controller->implData = (void *)((intptr_t)_contextId); std::vector derivedStateValue; derivedStateValue.resize(derivedState.length); [derivedState getBytes:derivedStateValue.data() length:derivedState.length]; - _controller->SetPersistentState(derivedStateValue); + std::unique_ptr proxyValue = nullptr; if (proxy != nil) { - _controller->SetProxy(tgvoip::PROXY_SOCKS5, proxy.host.UTF8String, (uint16_t)proxy.port, proxy.username.UTF8String ?: "", proxy.password.UTF8String ?: ""); + TgVoipProxy *proxyObject = new TgVoipProxy(); + proxyObject->host = proxy.host.UTF8String; + proxyObject->port = (uint16_t)proxy.port; + proxyObject->login = proxy.username.UTF8String ?: ""; + proxyObject->password = proxy.password.UTF8String ?: ""; + proxyValue = std::unique_ptr(proxyObject); } - auto callbacks = tgvoip::VoIPController::Callbacks(); - callbacks.connectionStateChanged = &controllerStateCallback; - callbacks.groupCallKeyReceived = NULL; - callbacks.groupCallKeySent = NULL; - callbacks.signalBarCountChanged = &signalBarsCallback; - callbacks.upgradeToGroupCallRequested = NULL; - _controller->SetCallbacks(callbacks); + 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; - tgvoip::VoIPController::crypto.sha1 = &TGCallSha1; - tgvoip::VoIPController::crypto.sha256 = &TGCallSha256; - tgvoip::VoIPController::crypto.rand_bytes = &TGCallRandomBytes; - tgvoip::VoIPController::crypto.aes_ige_encrypt = &TGCallAesIgeEncrypt; - tgvoip::VoIPController::crypto.aes_ige_decrypt = &TGCallAesIgeDecrypt; - tgvoip::VoIPController::crypto.aes_ctr_encrypt = &TGCallAesCtrEncrypt; + std::vector endpoints; + NSArray *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections]; + for (OngoingCallConnectionDescription *connection in connections) { + unsigned char peerTag[16]; + [connection.peerTag getBytes:peerTag length:16]; + + TgVoipEndpoint endpoint; + endpoint.endpointId = connection.connectionId; + endpoint.host = { + .ipv4 = std::string(connection.ip.UTF8String), + .ipv6 = std::string(connection.ipv6.UTF8String) + }; + endpoint.port = (uint16_t)connection.port; + endpoint.type = TgVoipEndpointType::UdpRelay; + memcpy(endpoint.peerTag, peerTag, 16); + endpoints.push_back(endpoint); + } + + TgVoipConfig config = { + .initializationTimeout = _callConnectTimeout, + .receiveTimeout = _callPacketTimeout, + .dataSaving = callControllerDataSavingForType(dataSaving), + .enableP2P = allowP2P, + .enableAEC = false, + .enableNS = true, + .enableAGC = true, + .enableCallUpgrade = false, + .logPath = logPath.length == 0 ? "" : std::string(logPath.UTF8String), + .maxApiLayer = [OngoingCallThreadLocalContext maxLayer] + }; + + std::vector encryptionKeyValue; + encryptionKeyValue.resize(key.length); + memcpy(encryptionKeyValue.data(), key.bytes, key.length); + + TgVoipEncryptionKey encryptionKey = { + .value = encryptionKeyValue, + .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 + ); _state = OngoingCallStateInitializing; _signalBars = -1; + + __weak OngoingCallThreadLocalContext *weakSelf = self; + _tgVoip->setOnStateUpdated([weakSelf](TgVoipState state) { + __strong OngoingCallThreadLocalContext *strongSelf = weakSelf; + if (strongSelf) { + [strongSelf controllerStateChanged:state]; + } + }); + _tgVoip->setOnSignalBarsUpdated([weakSelf](int signalBars) { + __strong OngoingCallThreadLocalContext *strongSelf = weakSelf; + if (strongSelf) { + [strongSelf signalBarsChanged:signalBars]; + } + }); } return self; } @@ -267,79 +329,30 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { - (void)dealloc { assert([_queue isCurrent]); removeContext(_contextId); - if (_controller != NULL) { + if (_tgVoip != NULL) { [self stop:nil]; } } -- (void)startWithKey:(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 { - std::vector endpoints; - NSArray *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections]; - for (OngoingCallConnectionDescription *connection in connections) { - struct in_addr addrIpV4; - if (!inet_aton(connection.ip.UTF8String, &addrIpV4)) { - NSLog(@"CallSession: invalid ipv4 address"); - } - - struct in6_addr addrIpV6; - if (!inet_pton(AF_INET6, connection.ipv6.UTF8String, &addrIpV6)) { - NSLog(@"CallSession: invalid ipv6 address"); - } - - tgvoip::IPv4Address address(std::string(connection.ip.UTF8String)); - tgvoip::IPv6Address addressv6(std::string(connection.ipv6.UTF8String)); - unsigned char peerTag[16]; - [connection.peerTag getBytes:peerTag length:16]; - endpoints.push_back(tgvoip::Endpoint(connection.connectionId, (uint16_t)connection.port, address, addressv6, tgvoip::Endpoint::Type::UDP_RELAY, peerTag)); - } - - tgvoip::VoIPController::Config config(_callConnectTimeout, _callPacketTimeout, _dataSavingMode, false, true, true); - config.logFilePath = logPath.length > 0 ? std::string(logPath.UTF8String) : ""; - config.statsDumpFilePath = ""; - - if (_controller != nil) { - _controller->SetConfig(config); - - _controller->SetNetworkType(callControllerNetworkTypeForType(_networkType)); - _controller->SetEncryptionKey((char *)key.bytes, isOutgoing); - _controller->SetRemoteEndpoints(endpoints, allowP2P, maxLayer); - _controller->Start(); - - _controller->Connect(); - } -} - - (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion { - if (_controller != nil) { - _controller->Stop(); + if (_tgVoip) { + TgVoipFinalState finalState = _tgVoip->stop(); - auto debugString = _controller->GetDebugLog(); - NSString *debugLog = [NSString stringWithUTF8String:debugString.c_str()]; + NSString *debugLog = [NSString stringWithUTF8String:finalState.debugLog.c_str()]; + _lastDerivedState = [[NSData alloc] initWithBytes:finalState.persistentState.value.data() length:finalState.persistentState.value.size()]; - tgvoip::VoIPController::TrafficStats stats; - _controller->GetStats(&stats); - std::vector derivedStateValue = _controller->GetPersistentState(); - _lastDerivedState = [[NSData alloc] initWithBytes:derivedStateValue.data() length:derivedStateValue.size()]; - delete _controller; - _controller = NULL; + delete _tgVoip; + _tgVoip = NULL; if (completion) { - completion(debugLog, stats.bytesSentWifi, stats.bytesRecvdWifi, stats.bytesSentMobile, stats.bytesRecvdMobile); + completion(debugLog, finalState.trafficStats.bytesSentWifi, finalState.trafficStats.bytesReceivedWifi, finalState.trafficStats.bytesSentMobile, finalState.trafficStats.bytesReceivedMobile); } } } - -- (bool)needRate { - if (_controller != nil) { - return _controller->NeedRate(); - } else { - return false; - } -} - (NSString *)debugInfo { - if (_controller != nil) { - auto rawDebugString = _controller->GetDebugString(); + if (_tgVoip != nil) { + auto rawDebugString = _tgVoip->getDebugInfo(); return [NSString stringWithUTF8String:rawDebugString.c_str()]; } else { return nil; @@ -347,17 +360,17 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { } - (NSString *)version { - if (_controller != nil) { - return [NSString stringWithUTF8String:_controller->GetVersion()]; + if (_tgVoip != nil) { + return [NSString stringWithUTF8String:_tgVoip->getVersion().c_str()]; } else { return nil; } } - (NSData * _Nonnull)getDerivedState { - if (_controller != nil) { - std::vector derivedStateValue = _controller->GetPersistentState(); - return [[NSData alloc] initWithBytes:derivedStateValue.data() length:derivedStateValue.size()]; + if (_tgVoip) { + auto persistentState = _tgVoip->getPersistentState(); + return [[NSData alloc] initWithBytes:persistentState.value.data() length:persistentState.value.size()]; } else if (_lastDerivedState != nil) { return _lastDerivedState; } else { @@ -365,16 +378,16 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { } } -- (void)controllerStateChanged:(int)state { +- (void)controllerStateChanged:(TgVoipState)state { OngoingCallState callState = OngoingCallStateInitializing; switch (state) { - case tgvoip::STATE_ESTABLISHED: + case TgVoipState::Estabilished: callState = OngoingCallStateConnected; break; - case tgvoip::STATE_FAILED: + case TgVoipState::Failed: callState = OngoingCallStateFailed; break; - case tgvoip::STATE_RECONNECTING: + case TgVoipState::Reconnecting: callState = OngoingCallStateReconnecting; break; default: @@ -401,16 +414,16 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { } - (void)setIsMuted:(bool)isMuted { - if (_controller != nil) { - _controller->SetMicMute(isMuted); + if (_tgVoip) { + _tgVoip->setMuteMicrophone(isMuted); } } - (void)setNetworkType:(OngoingCallNetworkType)networkType { if (_networkType != networkType) { _networkType = networkType; - if (_controller != nil) { - _controller->SetNetworkType(callControllerNetworkTypeForType(networkType)); + if (_tgVoip) { + _tgVoip->setNetworkType(callControllerNetworkTypeForType(networkType)); } } } diff --git a/submodules/TgVoip/libtgvoip b/submodules/TgVoip/libtgvoip index 522550a1e9..a045c9eea4 160000 --- a/submodules/TgVoip/libtgvoip +++ b/submodules/TgVoip/libtgvoip @@ -1 +1 @@ -Subproject commit 522550a1e975b17e9048d7a2ab2d5b97cfc2f5d4 +Subproject commit a045c9eea47b371c0c514c72c76172a211c894cb diff --git a/submodules/TgVoipWebrtc/BUCK b/submodules/TgVoipWebrtc/BUCK new file mode 100644 index 0000000000..ff3c426073 --- /dev/null +++ b/submodules/TgVoipWebrtc/BUCK @@ -0,0 +1,76 @@ +load("//Config:buck_rule_macros.bzl", "static_library", "glob_map", "glob_sub_map", "merge_maps") + +static_library( + name = "TgVoipWebrtc", + srcs = glob([ + "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*"]), + has_cpp = True, + headers = merge_maps([ + glob_sub_map("libtgvoip/", [ + "libtgvoip/*.h", + "libtgvoip/*.hpp", + "libtgvoip/audio/*.h", + "libtgvoip/audio/*.hpp", + "libtgvoip/video/*.h", + "libtgvoip/video/*.hpp", + ]), + glob_sub_map("libtgvoip/", [ + "libtgvoip/os/darwin/*.h", + ], exclude = ["libtgvoip/os/darwin/*OSX*"]), + glob_sub_map("libtgvoip/webrtc_dsp/", [ + "libtgvoip/webrtc_dsp/**/*.h", + ]), + ]), + exported_headers = glob([ + "PublicHeaders/**/*.h", + ]), + platform_compiler_flags = [ + ('arm.*', [ + '-DTGVOIP_USE_CUSTOM_CRYPTO', + '-DTGVOIP_USE_INSTALLED_OPUS', + '-DWEBRTC_APM_DEBUG_DUMP=0', + '-DWEBRTC_POSIX', + '-DTGVOIP_HAVE_TGLOG', + '-DWEBRTC_NS_FLOAT', + '-DWEBRTC_IOS', + '-DWEBRTC_HAS_NEON', + ]), + ('.*', [ + '-DTGVOIP_USE_CUSTOM_CRYPTO', + '-DTGVOIP_USE_INSTALLED_OPUS', + '-DWEBRTC_APM_DEBUG_DUMP=0', + '-DWEBRTC_POSIX', + '-DTGVOIP_HAVE_TGLOG', + '-DWEBRTC_NS_FLOAT', + '-DWEBRTC_IOS', + ]), + ], + deps = [ + "//submodules/MtProtoKit:MtProtoKit#shared", + "//submodules/Opus:opus", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Foundation.framework", + "$SDKROOT/System/Library/Frameworks/UIKit.framework", + "$SDKROOT/System/Library/Frameworks/AudioToolbox.framework", + "$SDKROOT/System/Library/Frameworks/VideoToolbox.framework", + "$SDKROOT/System/Library/Frameworks/CoreTelephony.framework", + "$SDKROOT/System/Library/Frameworks/CoreMedia.framework", + "$SDKROOT/System/Library/Frameworks/AVFoundation.framework", + ], +) \ No newline at end of file diff --git a/submodules/TgVoipWebrtc/BUILD b/submodules/TgVoipWebrtc/BUILD new file mode 100644 index 0000000000..41f0219225 --- /dev/null +++ b/submodules/TgVoipWebrtc/BUILD @@ -0,0 +1,74 @@ + +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", +] + +objc_library( + name = "TgVoipWebrtc", + enable_modules = True, + module_name = "TgVoipWebrtc", + srcs = glob([ + "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*"]), + 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, + }), + includes = [ + "PublicHeaders", + ], + deps = [ + "//submodules/MtProtoKit:MtProtoKit", + "//submodules/Opus:opus", + ], + sdk_frameworks = [ + "Foundation", + "UIKit", + "AudioToolbox", + "VideoToolbox", + "CoreTelephony", + "CoreMedia", + "AVFoundation", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h new file mode 100644 index 0000000000..d7f6e16846 --- /dev/null +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h @@ -0,0 +1,81 @@ +#ifndef OngoingCallContext_h +#define OngoingCallContext_h + +#import + +@interface OngoingCallConnectionDescription : NSObject + +@property (nonatomic, readonly) int64_t connectionId; +@property (nonatomic, strong, readonly) NSString * _Nonnull ip; +@property (nonatomic, strong, readonly) NSString * _Nonnull ipv6; +@property (nonatomic, readonly) int32_t port; +@property (nonatomic, strong, readonly) NSData * _Nonnull peerTag; + +- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag; + +@end + +typedef NS_ENUM(int32_t, OngoingCallState) { + OngoingCallStateInitializing, + OngoingCallStateConnected, + OngoingCallStateFailed, + OngoingCallStateReconnecting +}; + +typedef NS_ENUM(int32_t, OngoingCallNetworkType) { + OngoingCallNetworkTypeWifi, + OngoingCallNetworkTypeCellularGprs, + OngoingCallNetworkTypeCellularEdge, + OngoingCallNetworkTypeCellular3g, + OngoingCallNetworkTypeCellularLte +}; + +typedef NS_ENUM(int32_t, OngoingCallDataSaving) { + OngoingCallDataSavingNever, + OngoingCallDataSavingCellular, + OngoingCallDataSavingAlways +}; + +@protocol OngoingCallThreadLocalContextQueue + +- (void)dispatch:(void (^ _Nonnull)())f; +- (bool)isCurrent; + +@end + +@interface VoipProxyServer : NSObject + +@property (nonatomic, strong, readonly) NSString * _Nonnull host; +@property (nonatomic, readonly) int32_t port; +@property (nonatomic, strong, readonly) NSString * _Nullable username; +@property (nonatomic, strong, readonly) NSString * _Nullable password; + +- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password; + +@end + +@interface OngoingCallThreadLocalContext : 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 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; + +- (bool)needRate; + +- (NSString * _Nullable)debugInfo; +- (NSString * _Nullable)version; +- (NSData * _Nonnull)getDerivedState; + +- (void)setIsMuted:(bool)isMuted; +- (void)setNetworkType:(OngoingCallNetworkType)networkType; + +@end + +#endif diff --git a/submodules/TgVoip/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm similarity index 54% rename from submodules/TgVoip/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.mm rename to submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 125a7b849a..e74531bdee 100644 --- a/submodules/TgVoip/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -1,14 +1,9 @@ #import "OngoingCallThreadLocalContext.h" -#import "VoIPController.h" -#import "VoIPServerConfig.h" -#import "os/darwin/SetupLogging.h" +#import "TgVoip.h" -#ifdef BUCK #import -#else -#import -#endif +#include static void TGCallAesIgeEncrypt(uint8_t *inBytes, uint8_t *outBytes, size_t length, uint8_t *key, uint8_t *iv) { MTAesEncryptRaw(inBytes, outBytes, length, key, iv); @@ -131,34 +126,19 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte NSTimeInterval _callRingTimeout; NSTimeInterval _callConnectTimeout; NSTimeInterval _callPacketTimeout; - int32_t _dataSavingMode; - tgvoip::VoIPController *_controller; + TgVoip *_tgVoip; OngoingCallState _state; int32_t _signalBars; NSData *_lastDerivedState; } -- (void)controllerStateChanged:(int)state; +- (void)controllerStateChanged:(TgVoipState)state; - (void)signalBarsChanged:(int32_t)signalBars; @end -static void controllerStateCallback(tgvoip::VoIPController *controller, int state) { - int32_t contextId = (int32_t)((intptr_t)controller->implData); - withContext(contextId, ^(OngoingCallThreadLocalContext *context) { - [context controllerStateChanged:state]; - }); -} - -static void signalBarsCallback(tgvoip::VoIPController *controller, int signalBars) { - int32_t contextId = (int32_t)((intptr_t)controller->implData); - withContext(contextId, ^(OngoingCallThreadLocalContext *context) { - [context signalBarsChanged:(int32_t)signalBars]; - }); -} - @implementation VoipProxyServer - (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password { @@ -174,55 +154,62 @@ static void signalBarsCallback(tgvoip::VoIPController *controller, int signalBar @end -static int callControllerNetworkTypeForType(OngoingCallNetworkType type) { +static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkType type) { switch (type) { - case OngoingCallNetworkTypeWifi: - return tgvoip::NET_TYPE_WIFI; - case OngoingCallNetworkTypeCellularGprs: - return tgvoip::NET_TYPE_GPRS; - case OngoingCallNetworkTypeCellular3g: - return tgvoip::NET_TYPE_3G; - case OngoingCallNetworkTypeCellularLte: - return tgvoip::NET_TYPE_LTE; - default: - return tgvoip::NET_TYPE_WIFI; + case OngoingCallNetworkTypeWifi: + return TgVoipNetworkType::WiFi; + case OngoingCallNetworkTypeCellularGprs: + return TgVoipNetworkType::Gprs; + case OngoingCallNetworkTypeCellular3g: + return TgVoipNetworkType::ThirdGeneration; + case OngoingCallNetworkTypeCellularLte: + return TgVoipNetworkType::Lte; + default: + return TgVoipNetworkType::ThirdGeneration; } } -static int callControllerDataSavingForType(OngoingCallDataSaving type) { +static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSaving type) { switch (type) { - case OngoingCallDataSavingNever: - return tgvoip::DATA_SAVING_NEVER; - case OngoingCallDataSavingCellular: - return tgvoip::DATA_SAVING_MOBILE; - case OngoingCallDataSavingAlways: - return tgvoip::DATA_SAVING_ALWAYS; - default: - return tgvoip::DATA_SAVING_NEVER; + case OngoingCallDataSavingNever: + return TgVoipDataSaving::Never; + case OngoingCallDataSavingCellular: + return TgVoipDataSaving::Mobile; + case OngoingCallDataSavingAlways: + return TgVoipDataSaving::Always; + default: + return TgVoipDataSaving::Never; } } @implementation OngoingCallThreadLocalContext +static void (*InternalVoipLoggingFunction)(NSString *) = NULL; + + (void)setupLoggingFunction:(void (*)(NSString *))loggingFunction { - TGVoipLoggingFunction = loggingFunction; + InternalVoipLoggingFunction = loggingFunction; + TgVoip::setLoggingFunction([](std::string const &string) { + if (InternalVoipLoggingFunction) { + InternalVoipLoggingFunction([[NSString alloc] initWithUTF8String:string.c_str()]); + } + }); } + (void)applyServerConfig:(NSString *)string { if (string.length != 0) { - tgvoip::ServerConfig::GetSharedInstance()->Update(std::string(string.UTF8String)); + TgVoip::setGlobalServerConfig(std::string(string.UTF8String)); } } + (int32_t)maxLayer { - return tgvoip::VoIPController::GetConnectionMaxLayer(); + return 92; } + (NSString *)version { - return [NSString stringWithUTF8String:tgvoip::VoIPController::GetVersion()]; + return [NSString stringWithUTF8String:TgVoip::getVersion().c_str()]; } -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving derivedState:(NSData * _Nonnull)derivedState { +- (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 { self = [super init]; if (self != nil) { _queue = queue; @@ -233,37 +220,108 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { _callRingTimeout = 90.0; _callConnectTimeout = 30.0; _callPacketTimeout = 10.0; - _dataSavingMode = callControllerDataSavingForType(dataSaving); _networkType = networkType; - _controller = new tgvoip::VoIPController(); - _controller->implData = (void *)((intptr_t)_contextId); std::vector derivedStateValue; derivedStateValue.resize(derivedState.length); [derivedState getBytes:derivedStateValue.data() length:derivedState.length]; - _controller->SetPersistentState(derivedStateValue); + std::unique_ptr proxyValue = nullptr; if (proxy != nil) { - _controller->SetProxy(tgvoip::PROXY_SOCKS5, proxy.host.UTF8String, (uint16_t)proxy.port, proxy.username.UTF8String ?: "", proxy.password.UTF8String ?: ""); + TgVoipProxy *proxyObject = new TgVoipProxy(); + proxyObject->host = proxy.host.UTF8String; + proxyObject->port = (uint16_t)proxy.port; + proxyObject->login = proxy.username.UTF8String ?: ""; + proxyObject->password = proxy.password.UTF8String ?: ""; + proxyValue = std::unique_ptr(proxyObject); } - auto callbacks = tgvoip::VoIPController::Callbacks(); - callbacks.connectionStateChanged = &controllerStateCallback; - callbacks.groupCallKeyReceived = NULL; - callbacks.groupCallKeySent = NULL; - callbacks.signalBarCountChanged = &signalBarsCallback; - callbacks.upgradeToGroupCallRequested = NULL; - _controller->SetCallbacks(callbacks); + 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; - tgvoip::VoIPController::crypto.sha1 = &TGCallSha1; - tgvoip::VoIPController::crypto.sha256 = &TGCallSha256; - tgvoip::VoIPController::crypto.rand_bytes = &TGCallRandomBytes; - tgvoip::VoIPController::crypto.aes_ige_encrypt = &TGCallAesIgeEncrypt; - tgvoip::VoIPController::crypto.aes_ige_decrypt = &TGCallAesIgeDecrypt; - tgvoip::VoIPController::crypto.aes_ctr_encrypt = &TGCallAesCtrEncrypt; + std::vector endpoints; + NSArray *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections]; + for (OngoingCallConnectionDescription *connection in connections) { + unsigned char peerTag[16]; + [connection.peerTag getBytes:peerTag length:16]; + + TgVoipEndpoint endpoint; + endpoint.endpointId = connection.connectionId; + endpoint.host = { + .ipv4 = std::string(connection.ip.UTF8String), + .ipv6 = std::string(connection.ipv6.UTF8String) + }; + endpoint.port = (uint16_t)connection.port; + endpoint.type = TgVoipEndpointType::UdpRelay; + memcpy(endpoint.peerTag, peerTag, 16); + endpoints.push_back(endpoint); + } + + TgVoipConfig config = { + .initializationTimeout = _callConnectTimeout, + .receiveTimeout = _callPacketTimeout, + .dataSaving = callControllerDataSavingForType(dataSaving), + .enableP2P = allowP2P, + .enableAEC = false, + .enableNS = true, + .enableAGC = true, + .enableCallUpgrade = false, + .logPath = logPath.length == 0 ? "" : std::string(logPath.UTF8String), + .maxApiLayer = [OngoingCallThreadLocalContext maxLayer] + }; + + std::vector encryptionKeyValue; + encryptionKeyValue.resize(key.length); + memcpy(encryptionKeyValue.data(), key.bytes, key.length); + + TgVoipEncryptionKey encryptionKey = { + .value = encryptionKeyValue, + .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 + ); _state = OngoingCallStateInitializing; _signalBars = -1; + + __weak OngoingCallThreadLocalContext *weakSelf = self; + _tgVoip->setOnStateUpdated([weakSelf](TgVoipState state) { + __strong OngoingCallThreadLocalContext *strongSelf = weakSelf; + if (strongSelf) { + [strongSelf controllerStateChanged:state]; + } + }); + _tgVoip->setOnSignalBarsUpdated([weakSelf](int signalBars) { + __strong OngoingCallThreadLocalContext *strongSelf = weakSelf; + if (strongSelf) { + [strongSelf signalBarsChanged:signalBars]; + } + }); } return self; } @@ -271,79 +329,30 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { - (void)dealloc { assert([_queue isCurrent]); removeContext(_contextId); - if (_controller != NULL) { + if (_tgVoip != NULL) { [self stop:nil]; } } -- (void)startWithKey:(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 { - std::vector endpoints; - NSArray *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections]; - for (OngoingCallConnectionDescription *connection in connections) { - struct in_addr addrIpV4; - if (!inet_aton(connection.ip.UTF8String, &addrIpV4)) { - NSLog(@"CallSession: invalid ipv4 address"); - } - - struct in6_addr addrIpV6; - if (!inet_pton(AF_INET6, connection.ipv6.UTF8String, &addrIpV6)) { - NSLog(@"CallSession: invalid ipv6 address"); - } - - tgvoip::IPv4Address address(std::string(connection.ip.UTF8String)); - tgvoip::IPv6Address addressv6(std::string(connection.ipv6.UTF8String)); - unsigned char peerTag[16]; - [connection.peerTag getBytes:peerTag length:16]; - endpoints.push_back(tgvoip::Endpoint(connection.connectionId, (uint16_t)connection.port, address, addressv6, tgvoip::Endpoint::Type::UDP_RELAY, peerTag)); - } - - tgvoip::VoIPController::Config config(_callConnectTimeout, _callPacketTimeout, _dataSavingMode, false, true, true); - config.logFilePath = logPath.length > 0 ? std::string(logPath.UTF8String) : ""; - config.statsDumpFilePath = ""; - - if (_controller != nil) { - _controller->SetConfig(config); - - _controller->SetNetworkType(callControllerNetworkTypeForType(_networkType)); - _controller->SetEncryptionKey((char *)key.bytes, isOutgoing); - _controller->SetRemoteEndpoints(endpoints, allowP2P, maxLayer); - _controller->Start(); - - _controller->Connect(); - } -} - - (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion { - if (_controller != nil) { - _controller->Stop(); + if (_tgVoip) { + TgVoipFinalState finalState = _tgVoip->stop(); - auto debugString = _controller->GetDebugLog(); - NSString *debugLog = [NSString stringWithUTF8String:debugString.c_str()]; + NSString *debugLog = [NSString stringWithUTF8String:finalState.debugLog.c_str()]; + _lastDerivedState = [[NSData alloc] initWithBytes:finalState.persistentState.value.data() length:finalState.persistentState.value.size()]; - tgvoip::VoIPController::TrafficStats stats; - _controller->GetStats(&stats); - std::vector derivedStateValue = _controller->GetPersistentState(); - _lastDerivedState = [[NSData alloc] initWithBytes:derivedStateValue.data() length:derivedStateValue.size()]; - delete _controller; - _controller = NULL; + delete _tgVoip; + _tgVoip = NULL; if (completion) { - completion(debugLog, stats.bytesSentWifi, stats.bytesRecvdWifi, stats.bytesSentMobile, stats.bytesRecvdMobile); + completion(debugLog, finalState.trafficStats.bytesSentWifi, finalState.trafficStats.bytesReceivedWifi, finalState.trafficStats.bytesSentMobile, finalState.trafficStats.bytesReceivedMobile); } } } - -- (bool)needRate { - if (_controller != nil) { - return _controller->NeedRate(); - } else { - return false; - } -} - (NSString *)debugInfo { - if (_controller != nil) { - auto rawDebugString = _controller->GetDebugString(); + if (_tgVoip != nil) { + auto rawDebugString = _tgVoip->getDebugInfo(); return [NSString stringWithUTF8String:rawDebugString.c_str()]; } else { return nil; @@ -351,17 +360,17 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { } - (NSString *)version { - if (_controller != nil) { - return [NSString stringWithUTF8String:_controller->GetVersion()]; + if (_tgVoip != nil) { + return [NSString stringWithUTF8String:_tgVoip->getVersion().c_str()]; } else { return nil; } } - (NSData * _Nonnull)getDerivedState { - if (_controller != nil) { - std::vector derivedStateValue = _controller->GetPersistentState(); - return [[NSData alloc] initWithBytes:derivedStateValue.data() length:derivedStateValue.size()]; + if (_tgVoip) { + auto persistentState = _tgVoip->getPersistentState(); + return [[NSData alloc] initWithBytes:persistentState.value.data() length:persistentState.value.size()]; } else if (_lastDerivedState != nil) { return _lastDerivedState; } else { @@ -369,16 +378,16 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { } } -- (void)controllerStateChanged:(int)state { +- (void)controllerStateChanged:(TgVoipState)state { OngoingCallState callState = OngoingCallStateInitializing; switch (state) { - case tgvoip::STATE_ESTABLISHED: + case TgVoipState::Estabilished: callState = OngoingCallStateConnected; break; - case tgvoip::STATE_FAILED: + case TgVoipState::Failed: callState = OngoingCallStateFailed; break; - case tgvoip::STATE_RECONNECTING: + case TgVoipState::Reconnecting: callState = OngoingCallStateReconnecting; break; default: @@ -405,16 +414,16 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { } - (void)setIsMuted:(bool)isMuted { - if (_controller != nil) { - _controller->SetMicMute(isMuted); + if (_tgVoip) { + _tgVoip->setMuteMicrophone(isMuted); } } - (void)setNetworkType:(OngoingCallNetworkType)networkType { if (_networkType != networkType) { _networkType = networkType; - if (_controller != nil) { - _controller->SetNetworkType(callControllerNetworkTypeForType(networkType)); + if (_tgVoip) { + _tgVoip->setNetworkType(callControllerNetworkTypeForType(networkType)); } } }