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