Call updates

This commit is contained in:
Isaac 2025-02-25 14:43:41 +00:00
parent c37cc451ec
commit 92a6a73f6f
11 changed files with 306 additions and 1016 deletions

3
.gitmodules vendored
View File

@ -11,9 +11,6 @@ url=https://github.com/bazelbuild/rules_swift.git
[submodule "build-system/bazel-rules/apple_support"] [submodule "build-system/bazel-rules/apple_support"]
path = build-system/bazel-rules/apple_support path = build-system/bazel-rules/apple_support
url = https://github.com/bazelbuild/apple_support.git url = https://github.com/bazelbuild/apple_support.git
[submodule "submodules/TgVoip/libtgvoip"]
path = submodules/TgVoip/libtgvoip
url = https://github.com/telegramdesktop/libtgvoip.git
[submodule "submodules/TgVoipWebrtc/tgcalls"] [submodule "submodules/TgVoipWebrtc/tgcalls"]
path = submodules/TgVoipWebrtc/tgcalls path = submodules/TgVoipWebrtc/tgcalls
url=../tgcalls.git url=../tgcalls.git

View File

@ -15,7 +15,6 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/TelegramCore:TelegramCore", "//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramUIPreferences:TelegramUIPreferences", "//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/TgVoip:TgVoip",
"//submodules/TgVoipWebrtc:TgVoipWebrtc", "//submodules/TgVoipWebrtc:TgVoipWebrtc",
"//submodules/FFMpegBinding", "//submodules/FFMpegBinding",
"//submodules/ManagedFile", "//submodules/ManagedFile",

View File

@ -4,7 +4,6 @@ import TelegramCore
import Network import Network
import TelegramUIPreferences import TelegramUIPreferences
import TgVoip
import TgVoipWebrtc import TgVoipWebrtc
#if os(iOS) #if os(iOS)
@ -13,14 +12,6 @@ import AppBundle
import Accelerate import Accelerate
#endif #endif
private func debugUseLegacyVersionForReflectors() -> Bool {
#if DEBUG && false
return true
#else
return false
#endif
}
private struct PeerTag: Hashable, CustomStringConvertible { private struct PeerTag: Hashable, CustomStringConvertible {
var bytes: [UInt8] = Array<UInt8>(repeating: 0, count: 16) var bytes: [UInt8] = Array<UInt8>(repeating: 0, count: 16)
@ -167,11 +158,6 @@ private func cleanupCallLogs(account: Account) {
} }
private let setupLogs: Bool = { private let setupLogs: Bool = {
OngoingCallThreadLocalContext.setupLoggingFunction({ value in
if let value = value {
Logger.shared.log("TGVOIP", value)
}
})
OngoingCallThreadLocalContextWebrtc.setupLoggingFunction({ value in OngoingCallThreadLocalContextWebrtc.setupLoggingFunction({ value in
if let value = value { if let value = value {
Logger.shared.log("TGVOIP", value) Logger.shared.log("TGVOIP", value)
@ -253,26 +239,6 @@ private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCal
} }
} }
private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetworkType {
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 ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtc { private func ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtc {
switch type { switch type {
case .none: case .none:
@ -293,39 +259,6 @@ private func ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCall
} }
} }
/*private func ongoingNetworkTypeForTypeWebrtcCustom(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtcCustom {
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:
return .never
case .cellular:
return .cellular
case .always:
return .always
default:
return .never
}
}
private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtc { private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtc {
switch type { switch type {
case .never: case .never:
@ -363,56 +296,6 @@ private final class OngoingCallThreadLocalContextHolder {
} }
} }
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 nativeBeginTermination() {
}
func nativeSetIsMuted(_ value: Bool) {
self.setIsMuted(value)
}
func nativeSetIsLowBatteryLevel(_ value: Bool) {
}
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) {
}
func nativeSetRequestedVideoAspect(_ aspect: Float) {
}
func nativeDisableVideo() {
}
func nativeSwitchVideoCamera() {
}
func nativeDebugInfo() -> String {
return self.debugInfo() ?? ""
}
func nativeVersion() -> String {
return self.version() ?? ""
}
func nativeGetDerivedState() -> Data {
return self.getDerivedState()
}
func addExternalAudioData(data: Data) {
}
func nativeSetIsAudioSessionActive(isActive: Bool) {
}
}
#if targetEnvironment(simulator) #if targetEnvironment(simulator)
private extension UIImage { private extension UIImage {
@available(iOS 13.0, *) @available(iOS 13.0, *)
@ -816,23 +699,6 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
} }
} }
private extension OngoingCallContextState.State {
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.State { private extension OngoingCallContextState.State {
init(_ state: OngoingCallStateWebrtc) { init(_ state: OngoingCallStateWebrtc) {
switch state { switch state {
@ -1014,7 +880,7 @@ public final class OngoingCallContext {
private var networkTypeDisposable: Disposable? private var networkTypeDisposable: Disposable?
public static var maxLayer: Int32 { public static var maxLayer: Int32 {
return OngoingCallThreadLocalContext.maxLayer() return OngoingCallThreadLocalContextWebrtc.maxLayer()
} }
private let tempStatsLogFile: EngineTempBox.File private let tempStatsLogFile: EngineTempBox.File
@ -1024,26 +890,15 @@ public final class OngoingCallContext {
private let audioDevice: AudioDevice? private let audioDevice: AudioDevice?
public static func versions(includeExperimental: Bool, includeReference: Bool) -> [(version: String, supportsVideo: Bool)] { public static func versions(includeExperimental: Bool, includeReference: Bool) -> [(version: String, supportsVideo: Bool)] {
#if os(iOS) && DEBUG && false var result: [(version: String, supportsVideo: Bool)] = []
if "".isEmpty { result.append(contentsOf: OngoingCallThreadLocalContextWebrtc.versions(withIncludeReference: includeReference).map { version -> (version: String, supportsVideo: Bool) in
return [("5.0.0", true)] return (version, true)
} })
#endif return result
if debugUseLegacyVersionForReflectors() {
return [(OngoingCallThreadLocalContext.version(), true)]
} else {
var result: [(version: String, supportsVideo: Bool)] = [(OngoingCallThreadLocalContext.version(), false)]
result.append(contentsOf: OngoingCallThreadLocalContextWebrtc.versions(withIncludeReference: includeReference).map { version -> (version: String, supportsVideo: Bool) in
return (version, true)
})
return result
}
} }
public init(account: Account, callSessionManager: CallSessionManager, callId: CallId, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>, serializedData: String?, dataSaving: VoiceCallDataSaving, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowP2P: Bool, enableTCP: Bool, enableStunMarking: Bool, audioSessionActive: Signal<Bool, NoError>, logName: String, preferredVideoCodec: String?, audioDevice: AudioDevice?) { public init(account: Account, callSessionManager: CallSessionManager, callId: CallId, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>, serializedData: String?, dataSaving: VoiceCallDataSaving, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowP2P: Bool, enableTCP: Bool, enableStunMarking: Bool, audioSessionActive: Signal<Bool, NoError>, logName: String, preferredVideoCodec: String?, audioDevice: AudioDevice?) {
let _ = setupLogs let _ = setupLogs
OngoingCallThreadLocalContext.applyServerConfig(serializedData)
self.callId = callId self.callId = callId
self.internalId = internalId self.internalId = internalId
@ -1068,311 +923,274 @@ public final class OngoingCallContext {
|> take(1) |> take(1)
|> deliverOn(queue)).start(next: { [weak self] _ in |> deliverOn(queue)).start(next: { [weak self] _ in
if let strongSelf = self { if let strongSelf = self {
var useModernImplementation = true
var version = version
var allowP2P = allowP2P var allowP2P = allowP2P
if debugUseLegacyVersionForReflectors() {
useModernImplementation = true var voipProxyServer: VoipProxyServerWebrtc?
version = "12.0.0" if let proxyServer = proxyServer {
allowP2P = false switch proxyServer.connection {
} else { case let .socks5(username, password):
useModernImplementation = version != OngoingCallThreadLocalContext.version() voipProxyServer = VoipProxyServerWebrtc(host: proxyServer.host, port: proxyServer.port, username: username, password: password)
case .mtp:
break
}
} }
if useModernImplementation { var unfilteredConnections: [CallSessionConnection]
var voipProxyServer: VoipProxyServerWebrtc? unfilteredConnections = [connections.primary] + connections.alternatives
if let proxyServer = proxyServer {
switch proxyServer.connection { if version == "12.0.0" {
case let .socks5(username, password):
voipProxyServer = VoipProxyServerWebrtc(host: proxyServer.host, port: proxyServer.port, username: username, password: password)
case .mtp:
break
}
}
var unfilteredConnections: [CallSessionConnection]
unfilteredConnections = [connections.primary] + connections.alternatives
if version == "12.0.0" {
for connection in unfilteredConnections {
if case let .reflector(reflector) = connection {
unfilteredConnections.append(.reflector(CallSessionConnection.Reflector(
id: 123456,
ip: "91.108.9.38",
ipv6: "",
isTcp: true,
port: 595,
peerTag: reflector.peerTag
)))
}
}
}
var reflectorIdList: [Int64] = []
for connection in unfilteredConnections { for connection in unfilteredConnections {
switch connection { if case let .reflector(reflector) = connection {
case let .reflector(reflector): unfilteredConnections.append(.reflector(CallSessionConnection.Reflector(
reflectorIdList.append(reflector.id) id: 123456,
case .webRtcReflector: ip: "91.108.9.38",
break ipv6: "",
isTcp: true,
port: 595,
peerTag: reflector.peerTag
)))
} }
} }
}
reflectorIdList.sort()
var reflectorIdList: [Int64] = []
var reflectorIdMapping: [Int64: UInt8] = [:] for connection in unfilteredConnections {
for i in 0 ..< reflectorIdList.count { switch connection {
reflectorIdMapping[reflectorIdList[i]] = UInt8(i + 1) case let .reflector(reflector):
reflectorIdList.append(reflector.id)
case .webRtcReflector:
break
} }
}
reflectorIdList.sort()
var reflectorIdMapping: [Int64: UInt8] = [:]
for i in 0 ..< reflectorIdList.count {
reflectorIdMapping[reflectorIdList[i]] = UInt8(i + 1)
}
var signalingReflector: OngoingCallConnectionDescriptionWebrtc?
var processedConnections: [CallSessionConnection] = []
var filteredConnections: [OngoingCallConnectionDescriptionWebrtc] = []
connectionsLoop: for connection in unfilteredConnections {
if processedConnections.contains(connection) {
continue
}
processedConnections.append(connection)
var signalingReflector: OngoingCallConnectionDescriptionWebrtc? switch connection {
case let .reflector(reflector):
var processedConnections: [CallSessionConnection] = [] if reflector.isTcp {
var filteredConnections: [OngoingCallConnectionDescriptionWebrtc] = [] if version == "12.0.0" {
connectionsLoop: for connection in unfilteredConnections { /*if signalingReflector == nil {
if processedConnections.contains(connection) { signalingReflector = OngoingCallConnectionDescriptionWebrtc(reflectorId: 0, hasStun: false, hasTurn: true, hasTcp: true, ip: reflector.ip, port: reflector.port, username: "reflector", password: hexString(reflector.peerTag))
continue }*/
} } else {
processedConnections.append(connection) if signalingReflector == nil {
signalingReflector = OngoingCallConnectionDescriptionWebrtc(reflectorId: 0, hasStun: false, hasTurn: true, hasTcp: true, ip: reflector.ip, port: reflector.port, username: "reflector", password: hexString(reflector.peerTag))
switch connection {
case let .reflector(reflector):
if reflector.isTcp {
if version == "12.0.0" {
/*if signalingReflector == nil {
signalingReflector = OngoingCallConnectionDescriptionWebrtc(reflectorId: 0, hasStun: false, hasTurn: true, hasTcp: true, ip: reflector.ip, port: reflector.port, username: "reflector", password: hexString(reflector.peerTag))
}*/
} else {
if signalingReflector == nil {
signalingReflector = OngoingCallConnectionDescriptionWebrtc(reflectorId: 0, hasStun: false, hasTurn: true, hasTcp: true, ip: reflector.ip, port: reflector.port, username: "reflector", password: hexString(reflector.peerTag))
}
continue connectionsLoop
}
}
case .webRtcReflector:
break
}
var webrtcConnections: [OngoingCallConnectionDescriptionWebrtc] = []
for connection in callConnectionDescriptionsWebrtc(connection, idMapping: reflectorIdMapping) {
webrtcConnections.append(connection)
}
filteredConnections.append(contentsOf: webrtcConnections)
}
if let signalingReflector = signalingReflector {
if #available(iOS 12.0, *) {
let peerTag = dataWithHexString(signalingReflector.password)
strongSelf.signalingConnectionManager = QueueLocalObject(queue: queue, generate: {
return CallSignalingConnectionManager(queue: queue, peerTag: peerTag, servers: [signalingReflector], dataReceived: { data in
guard let strongSelf = self else {
return
}
strongSelf.withContext { context in
if let context = context as? OngoingCallThreadLocalContextWebrtc {
context.addSignaling(data)
}
}
})
})
}
}
var directConnection: OngoingCallDirectConnection?
if version == "9.0.0" && !"".isEmpty {
if #available(iOS 12.0, *) {
for connection in filteredConnections {
if connection.username == "reflector" && connection.reflectorId == 1 && !connection.hasTcp && connection.hasTurn {
directConnection = CallDirectConnectionImpl(host: connection.ip, port: Int(connection.port), peerTag: dataWithHexString(connection.password))
break
} }
continue connectionsLoop
} }
} }
} else { case .webRtcReflector:
directConnection = nil break
} }
#if DEBUG && true var webrtcConnections: [OngoingCallConnectionDescriptionWebrtc] = []
var customParameters = customParameters for connection in callConnectionDescriptionsWebrtc(connection, idMapping: reflectorIdMapping) {
if let initialCustomParameters = try? JSONSerialization.jsonObject(with: (customParameters ?? "{}").data(using: .utf8)!) as? [String: Any] { webrtcConnections.append(connection)
var customParametersValue: [String: Any] }
customParametersValue = initialCustomParameters
if version == "12.0.0" { filteredConnections.append(contentsOf: webrtcConnections)
customParametersValue["network_use_tcponly"] = true as NSNumber }
customParameters = String(data: try! JSONSerialization.data(withJSONObject: customParametersValue), encoding: .utf8)!
} if let signalingReflector = signalingReflector {
if #available(iOS 12.0, *) {
let peerTag = dataWithHexString(signalingReflector.password)
if let value = customParametersValue["network_use_tcponly"] as? Bool, value { strongSelf.signalingConnectionManager = QueueLocalObject(queue: queue, generate: {
filteredConnections = filteredConnections.filter { connection in return CallSignalingConnectionManager(queue: queue, peerTag: peerTag, servers: [signalingReflector], dataReceived: { data in
if connection.hasTcp {
return true
}
return false
}
allowP2P = false
}
}
#endif
/*#if DEBUG
if let initialCustomParameters = try? JSONSerialization.jsonObject(with: (customParameters ?? "{}").data(using: .utf8)!) as? [String: Any] {
var customParametersValue: [String: Any]
customParametersValue = initialCustomParameters
customParametersValue["network_kcp_experiment"] = true as NSNumber
customParameters = String(data: try! JSONSerialization.data(withJSONObject: customParametersValue), encoding: .utf8)!
}
#endif*/
let context = OngoingCallThreadLocalContextWebrtc(
version: version,
customParameters: customParameters,
queue: OngoingCallThreadLocalContextQueueImpl(queue: queue),
proxy: voipProxyServer,
networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType),
dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving),
derivedState: Data(),
key: key,
isOutgoing: isOutgoing,
connections: filteredConnections,
maxLayer: maxLayer,
allowP2P: allowP2P,
allowTCP: enableTCP,
enableStunMarking: enableStunMarking,
logPath: logPath,
statsLogPath: tempStatsLogPath,
sendSignalingData: { [weak callSessionManager] data in
queue.async {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if let signalingConnectionManager = strongSelf.signalingConnectionManager { strongSelf.withContext { context in
signalingConnectionManager.with { impl in if let context = context as? OngoingCallThreadLocalContextWebrtc {
impl.send(payloadData: data) context.addSignaling(data)
} }
} }
})
if let callSessionManager = callSessionManager { })
callSessionManager.sendSignalingData(internalId: internalId, data: data) }
} }
var directConnection: OngoingCallDirectConnection?
if version == "9.0.0" && !"".isEmpty {
if #available(iOS 12.0, *) {
for connection in filteredConnections {
if connection.username == "reflector" && connection.reflectorId == 1 && !connection.hasTcp && connection.hasTurn {
directConnection = CallDirectConnectionImpl(host: connection.ip, port: Int(connection.port), peerTag: dataWithHexString(connection.password))
break
} }
}, }
videoCapturer: video?.impl, }
preferredVideoCodec: preferredVideoCodec, } else {
audioInputDeviceId: "", directConnection = nil
audioDevice: audioDevice?.impl, }
directConnection: directConnection
) #if DEBUG && true
var customParameters = customParameters
if let initialCustomParameters = try? JSONSerialization.jsonObject(with: (customParameters ?? "{}").data(using: .utf8)!) as? [String: Any] {
var customParametersValue: [String: Any]
customParametersValue = initialCustomParameters
if version == "12.0.0" {
customParametersValue["network_use_tcponly"] = true as NSNumber
customParameters = String(data: try! JSONSerialization.data(withJSONObject: customParametersValue), encoding: .utf8)!
}
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) if let value = customParametersValue["network_use_tcponly"] as? Bool, value {
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in filteredConnections = filteredConnections.filter { connection in
if connection.hasTcp {
return true
}
return false
}
allowP2P = false
}
}
#endif
/*#if DEBUG
if let initialCustomParameters = try? JSONSerialization.jsonObject(with: (customParameters ?? "{}").data(using: .utf8)!) as? [String: Any] {
var customParametersValue: [String: Any]
customParametersValue = initialCustomParameters
customParametersValue["network_kcp_experiment"] = true as NSNumber
customParameters = String(data: try! JSONSerialization.data(withJSONObject: customParametersValue), encoding: .utf8)!
}
#endif*/
let context = OngoingCallThreadLocalContextWebrtc(
version: version,
customParameters: customParameters,
queue: OngoingCallThreadLocalContextQueueImpl(queue: queue),
proxy: voipProxyServer,
networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType),
dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving),
derivedState: Data(),
key: key,
isOutgoing: isOutgoing,
connections: filteredConnections,
maxLayer: maxLayer,
allowP2P: allowP2P,
allowTCP: enableTCP,
enableStunMarking: enableStunMarking,
logPath: logPath,
statsLogPath: tempStatsLogPath,
sendSignalingData: { [weak callSessionManager] data in
queue.async { queue.async {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let mappedState = OngoingCallContextState.State(state) if let signalingConnectionManager = strongSelf.signalingConnectionManager {
let mappedVideoState: OngoingCallContextState.VideoState signalingConnectionManager.with { impl in
switch videoState { impl.send(payloadData: data)
case .inactive: }
mappedVideoState = .inactive
case .active:
mappedVideoState = .active
case .paused:
mappedVideoState = .paused
@unknown default:
mappedVideoState = .notAvailable
} }
let mappedRemoteVideoState: OngoingCallContextState.RemoteVideoState
switch remoteVideoState { if let callSessionManager = callSessionManager {
case .inactive: callSessionManager.sendSignalingData(internalId: internalId, data: data)
mappedRemoteVideoState = .inactive
case .active:
mappedRemoteVideoState = .active
case .paused:
mappedRemoteVideoState = .paused
@unknown default:
mappedRemoteVideoState = .inactive
} }
let mappedRemoteAudioState: OngoingCallContextState.RemoteAudioState
switch remoteAudioState {
case .active:
mappedRemoteAudioState = .active
case .muted:
mappedRemoteAudioState = .muted
@unknown default:
mappedRemoteAudioState = .active
}
let mappedRemoteBatteryLevel: OngoingCallContextState.RemoteBatteryLevel
switch remoteBatteryLevel {
case .normal:
mappedRemoteBatteryLevel = .normal
case .low:
mappedRemoteBatteryLevel = .low
@unknown default:
mappedRemoteBatteryLevel = .normal
}
if case .active = mappedVideoState, !strongSelf.didReportCallAsVideo {
strongSelf.didReportCallAsVideo = true
callSessionManager?.updateCallType(internalId: internalId, type: .video)
}
strongSelf.contextState.set(.single(OngoingCallContextState(state: mappedState, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)))
} }
} },
strongSelf.receptionPromise.set(.single(4)) videoCapturer: video?.impl,
context.signalBarsChanged = { signalBars in preferredVideoCodec: preferredVideoCodec,
self?.receptionPromise.set(.single(signalBars)) audioInputDeviceId: "",
} audioDevice: audioDevice?.impl,
context.audioLevelUpdated = { level in directConnection: directConnection
self?.audioLevelPromise.set(.single(level)) )
}
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
if audioDevice == nil { context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
strongSelf.audioSessionActiveDisposable.set((audioSessionActive queue.async {
|> deliverOn(queue)).start(next: { isActive in guard let strongSelf = self else {
guard let strongSelf = self else { return
return
}
strongSelf.withContext { context in
context.nativeSetIsAudioSessionActive(isActive: isActive)
}
}))
}
strongSelf.networkTypeDisposable = (updatedNetworkType
|> deliverOn(queue)).start(next: { networkType in
self?.withContext { context in
context.nativeSetNetworkType(networkType)
} }
}) let mappedState = OngoingCallContextState.State(state)
} else { let mappedVideoState: OngoingCallContextState.VideoState
var voipProxyServer: VoipProxyServer? switch videoState {
if let proxyServer = proxyServer { case .inactive:
switch proxyServer.connection { mappedVideoState = .inactive
case let .socks5(username, password): case .active:
voipProxyServer = VoipProxyServer(host: proxyServer.host, port: proxyServer.port, username: username, password: password) mappedVideoState = .active
case .mtp: case .paused:
break mappedVideoState = .paused
@unknown default:
mappedVideoState = .notAvailable
} }
} let mappedRemoteVideoState: OngoingCallContextState.RemoteVideoState
let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: Data(), key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary)!, alternativeConnections: connections.alternatives.compactMap(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath) switch remoteVideoState {
case .inactive:
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) mappedRemoteVideoState = .inactive
context.stateChanged = { state in case .active:
self?.contextState.set(.single(OngoingCallContextState(state: OngoingCallContextState.State(state), videoState: .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))) mappedRemoteVideoState = .active
} case .paused:
context.signalBarsChanged = { signalBars in mappedRemoteVideoState = .paused
self?.receptionPromise.set(.single(signalBars)) @unknown default:
} mappedRemoteVideoState = .inactive
strongSelf.networkTypeDisposable = (updatedNetworkType
|> deliverOn(queue)).start(next: { networkType in
self?.withContext { context in
context.nativeSetNetworkType(networkType)
} }
}) let mappedRemoteAudioState: OngoingCallContextState.RemoteAudioState
switch remoteAudioState {
case .active:
mappedRemoteAudioState = .active
case .muted:
mappedRemoteAudioState = .muted
@unknown default:
mappedRemoteAudioState = .active
}
let mappedRemoteBatteryLevel: OngoingCallContextState.RemoteBatteryLevel
switch remoteBatteryLevel {
case .normal:
mappedRemoteBatteryLevel = .normal
case .low:
mappedRemoteBatteryLevel = .low
@unknown default:
mappedRemoteBatteryLevel = .normal
}
if case .active = mappedVideoState, !strongSelf.didReportCallAsVideo {
strongSelf.didReportCallAsVideo = true
callSessionManager?.updateCallType(internalId: internalId, type: .video)
}
strongSelf.contextState.set(.single(OngoingCallContextState(state: mappedState, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)))
}
} }
strongSelf.receptionPromise.set(.single(4))
context.signalBarsChanged = { signalBars in
self?.receptionPromise.set(.single(signalBars))
}
context.audioLevelUpdated = { level in
self?.audioLevelPromise.set(.single(level))
}
if audioDevice == nil {
strongSelf.audioSessionActiveDisposable.set((audioSessionActive
|> deliverOn(queue)).start(next: { isActive in
guard let strongSelf = self else {
return
}
strongSelf.withContext { context in
context.nativeSetIsAudioSessionActive(isActive: isActive)
}
}))
}
strongSelf.networkTypeDisposable = (updatedNetworkType
|> deliverOn(queue)).start(next: { networkType in
self?.withContext { context in
context.nativeSetNetworkType(networkType)
}
})
strongSelf.signalingDataDisposable = callSessionManager.beginReceivingCallSignalingData(internalId: internalId, { [weak self] dataList in strongSelf.signalingDataDisposable = callSessionManager.beginReceivingCallSignalingData(internalId: internalId, { [weak self] dataList in
queue.async { queue.async {

View File

@ -1,82 +0,0 @@
copts_arm = [
"-DTGVOIP_USE_CUSTOM_CRYPTO",
"-DWEBRTC_APM_DEBUG_DUMP=0",
"-DWEBRTC_POSIX",
"-DTGVOIP_HAVE_TGLOG",
"-DWEBRTC_NS_FLOAT",
"-DWEBRTC_IOS",
"-DWEBRTC_HAS_NEON",
"-DTGVOIP_NO_DSP",
]
copts_x86 = [
"-DTGVOIP_USE_CUSTOM_CRYPTO",
"-DWEBRTC_APM_DEBUG_DUMP=0",
"-DWEBRTC_POSIX",
"-DTGVOIP_HAVE_TGLOG",
"-DTGVOIP_NO_DSP",
"-DWEBRTC_NS_FLOAT",
"-DWEBRTC_IOS",
]
objc_library(
name = "TgVoip",
enable_modules = True,
module_name = "TgVoip",
srcs = glob([
"Sources/**/*.m",
"Sources/**/*.mm",
"Sources/**/*.h",
"libtgvoip/*.h",
"libtgvoip/*.hpp",
"libtgvoip/*.m",
"libtgvoip/*.mm",
"libtgvoip/*.cpp",
"libtgvoip/audio/*.h",
"libtgvoip/audio/*.cpp",
"libtgvoip/video/*.h",
"libtgvoip/video/*.cpp",
"libtgvoip/os/darwin/*.h",
"libtgvoip/os/darwin/*.m",
"libtgvoip/os/darwin/*.mm",
"libtgvoip/os/darwin/*.cpp",
"libtgvoip/os/posix/*.h",
"libtgvoip/os/posix/*.cpp",
], exclude = ["libtgvoip/os/darwin/*OSX*"]),
hdrs = glob([
"PublicHeaders/**/*.h",
]),
copts = [
"-I{}/PublicHeaders/TgVoip".format(package_name()),
"-I{}/libtgvoip".format(package_name()),
"-I{}/third-party/webrtc/webrtc".format(package_name()),
"-Isubmodules/Opus/Public/opus",
"-DTGVOIP_USE_INSTALLED_OPUS",
"-Drtc=rtc1",
"-Dwebrtc=webrtc1",
] + select({
"@build_bazel_rules_apple//apple:ios_arm64": copts_arm,
"//build-system:ios_sim_arm64": copts_arm,
"@build_bazel_rules_apple//apple:ios_x86_64": copts_x86,
}),
includes = [
"PublicHeaders",
],
deps = [
"//submodules/MtProtoKit:MtProtoKit",
"//third-party/opus:opus",
],
sdk_frameworks = [
"Foundation",
"UIKit",
"AudioToolbox",
"VideoToolbox",
"CoreTelephony",
"CoreMedia",
"AVFoundation",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,81 +0,0 @@
#ifndef OngoingCallContext_h
#define OngoingCallContext_h
#import <Foundation/Foundation.h>
@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 <NSObject>
- (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<OngoingCallThreadLocalContextQueue> _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<OngoingCallConnectionDescription *> * _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

View File

@ -1,419 +0,0 @@
#import "OngoingCallThreadLocalContext.h"
#import "TgVoip.h"
#import <MtProtoKit/MtProtoKit.h>
#include <memory>
#import <libkern/OSAtomic.h>
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
- (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];
if (self != nil) {
_connectionId = connectionId;
_ip = ip;
_ipv6 = ipv6;
_port = port;
_peerTag = peerTag;
}
return self;
}
@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<OngoingCallThreadLocalContextQueue> queue;
@end
@implementation OngoingCallThreadLocalContextReference
- (instancetype)initWithContext:(OngoingCallThreadLocalContext *)context queue:(id<OngoingCallThreadLocalContextQueue>)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<OngoingCallThreadLocalContextQueue> 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<OngoingCallThreadLocalContextQueue> _queue;
int32_t _contextId;
OngoingCallNetworkType _networkType;
NSTimeInterval _callReceiveTimeout;
NSTimeInterval _callRingTimeout;
NSTimeInterval _callConnectTimeout;
NSTimeInterval _callPacketTimeout;
std::unique_ptr<TgVoip> _tgVoip;
OngoingCallState _state;
int32_t _signalBars;
NSData *_lastDerivedState;
}
- (void)controllerStateChanged:(TgVoipState)state;
- (void)signalBarsChanged:(int32_t)signalBars;
@end
@implementation VoipProxyServer
- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password {
self = [super init];
if (self != nil) {
_host = host;
_port = port;
_username = username;
_password = password;
}
return self;
}
@end
static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkType type) {
switch (type) {
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 TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSaving type) {
switch (type) {
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 {
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::setGlobalServerConfig(std::string(string.UTF8String));
}
}
+ (int32_t)maxLayer {
return 92;
}
+ (NSString *)version {
return [NSString stringWithUTF8String:TgVoip::getVersion().c_str()];
}
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueue> _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<OngoingCallConnectionDescription *> * _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;
_callConnectTimeout = 30.0;
_callPacketTimeout = 10.0;
_networkType = networkType;
std::vector<uint8_t> derivedStateValue;
derivedStateValue.resize(derivedState.length);
[derivedState getBytes:derivedStateValue.data() length:derivedState.length];
std::unique_ptr<TgVoipProxy> proxyValue = nullptr;
if (proxy != nil) {
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<TgVoipProxy>(proxyObject);
}
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;
std::vector<TgVoipEndpoint> endpoints;
NSArray<OngoingCallConnectionDescription *> *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;
config.initializationTimeout = _callConnectTimeout;
config.receiveTimeout = _callPacketTimeout;
config.dataSaving = callControllerDataSavingForType(dataSaving);
config.enableP2P = static_cast<bool>(allowP2P);
config.enableAEC = false;
config.enableNS = true;
config.enableAGC = true;
config.enableVolumeControl = false;
config.enableCallUpgrade = false;
config.logPath = logPath.length == 0 ? "" : std::string(logPath.UTF8String);
config.maxApiLayer = [OngoingCallThreadLocalContext maxLayer];
std::vector<uint8_t> encryptionKeyValue;
encryptionKeyValue.resize(key.length);
memcpy(encryptionKeyValue.data(), key.bytes, key.length);
TgVoipEncryptionKey encryptionKey;
encryptionKey.value = encryptionKeyValue;
encryptionKey.isOutgoing = isOutgoing;
_tgVoip = TgVoip::makeInstance(
config,
{ derivedStateValue },
endpoints,
proxyValue.get(),
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;
}
- (void)dealloc {
assert([_queue isCurrent]);
removeContext(_contextId);
if (_tgVoip != NULL) {
[self stop:nil];
}
}
- (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
if (_tgVoip) {
TgVoipFinalState finalState = _tgVoip->stop();
NSString *debugLog = [NSString stringWithUTF8String:finalState.debugLog.c_str()];
_lastDerivedState = [[NSData alloc] initWithBytes:finalState.persistentState.value.data() length:finalState.persistentState.value.size()];
_tgVoip.reset();
if (completion) {
completion(debugLog, finalState.trafficStats.bytesSentWifi, finalState.trafficStats.bytesReceivedWifi, finalState.trafficStats.bytesSentMobile, finalState.trafficStats.bytesReceivedMobile);
}
}
}
- (NSString *)debugInfo {
if (_tgVoip != nil) {
auto rawDebugString = _tgVoip->getDebugInfo();
return [NSString stringWithUTF8String:rawDebugString.c_str()];
} else {
return nil;
}
}
- (NSString *)version {
if (_tgVoip != nil) {
return [NSString stringWithUTF8String:_tgVoip->getVersion().c_str()];
} else {
return nil;
}
}
- (NSData * _Nonnull)getDerivedState {
if (_tgVoip) {
auto persistentState = _tgVoip->getPersistentState();
return [[NSData alloc] initWithBytes:persistentState.value.data() length:persistentState.value.size()];
} else if (_lastDerivedState != nil) {
return _lastDerivedState;
} else {
return [NSData data];
}
}
- (void)controllerStateChanged:(TgVoipState)state {
OngoingCallState callState = OngoingCallStateInitializing;
switch (state) {
case TgVoipState::Established:
callState = OngoingCallStateConnected;
break;
case TgVoipState::Failed:
callState = OngoingCallStateFailed;
break;
case TgVoipState::Reconnecting:
callState = OngoingCallStateReconnecting;
break;
default:
break;
}
if (callState != _state) {
_state = callState;
if (_stateChanged) {
_stateChanged(callState);
}
}
}
- (void)signalBarsChanged:(int32_t)signalBars {
if (signalBars != _signalBars) {
_signalBars = signalBars;
if (_signalBarsChanged) {
_signalBarsChanged(signalBars);
}
}
}
- (void)setIsMuted:(bool)isMuted {
if (_tgVoip) {
_tgVoip->setMuteMicrophone(isMuted);
}
}
- (void)setNetworkType:(OngoingCallNetworkType)networkType {
if (_networkType != networkType) {
_networkType = networkType;
if (_tgVoip) {
_tgVoip->setNetworkType(callControllerNetworkTypeForType(networkType));
}
}
}
@end

@ -1 +0,0 @@
Subproject commit 37d98e984fd6fa389262307db826d52ab86c8241

View File

@ -11,6 +11,36 @@
#define UIView NSView #define UIView NSView
#endif #endif
@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
@protocol OngoingCallThreadLocalContextQueue <NSObject>
- (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 CallAudioTone : NSObject @interface CallAudioTone : NSObject
@property (nonatomic, strong, readonly) NSData * _Nonnull samples; @property (nonatomic, strong, readonly) NSData * _Nonnull samples;

View File

@ -42,6 +42,38 @@
#import "platform/darwin/TGRTCCVPixelBuffer.h" #import "platform/darwin/TGRTCCVPixelBuffer.h"
#include "rtc_base/logging.h" #include "rtc_base/logging.h"
@implementation OngoingCallConnectionDescription
- (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];
if (self != nil) {
_connectionId = connectionId;
_ip = ip;
_ipv6 = ipv6;
_port = port;
_peerTag = peerTag;
}
return self;
}
@end
@implementation VoipProxyServer
- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password {
self = [super init];
if (self != nil) {
_host = host;
_port = port;
_username = username;
_password = password;
}
return self;
}
@end
@implementation CallAudioTone @implementation CallAudioTone
- (instancetype _Nonnull)initWithSamples:(NSData * _Nonnull)samples sampleRate:(NSInteger)sampleRate loopCount:(NSInteger)loopCount { - (instancetype _Nonnull)initWithSamples:(NSData * _Nonnull)samples sampleRate:(NSInteger)sampleRate loopCount:(NSInteger)loopCount {
@ -1488,9 +1520,6 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
} }
+ (void)applyServerConfig:(NSString *)string { + (void)applyServerConfig:(NSString *)string {
if (string.length != 0) {
//TgVoip::setGlobalServerConfig(std::string(string.UTF8String));
}
} }
+ (void)setupAudioSession { + (void)setupAudioSession {

@ -1 +1 @@
Subproject commit d50eeeb40ce6a2d36d505bcaf0b5f4e5ed473f22 Subproject commit 1ea67f88b8d9fd04fc151d164669f6c229d651d3

@ -1 +1 @@
Subproject commit 77d3d1fe2ff2f364e8edee58179a7b7b95239b01 Subproject commit e8d2ef8c74f07c4f952db43bdfc08f39228f79c3