mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +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"]
|
||||
path = build-system/tulsi
|
||||
url = https://github.com/ali-fareed/tulsi.git
|
||||
[submodule "third-party/depot_tools"]
|
||||
path = third-party/depot_tools
|
||||
url = https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
[submodule "third-party/webrtc/webrtc-ios"]
|
||||
path = third-party/webrtc/webrtc-ios
|
||||
url=../webrtc-ios.git
|
||||
|
@ -161,8 +161,6 @@ swift_library(
|
||||
"//submodules/PasswordSetupUI:PasswordSetupUIAssets",
|
||||
"//submodules/TelegramUI:TelegramUIResources",
|
||||
"//submodules/TelegramUI:TelegramUIAssets",
|
||||
#"//submodules/WalletUI:WalletUIResources",
|
||||
#"//submodules/WalletUI:WalletUIAssets",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/TelegramUI:TelegramUI",
|
||||
|
@ -17,7 +17,6 @@ swift_library(
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/SyncCore:SyncCore",
|
||||
"//submodules/WalletCore:WalletCore",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -79,7 +79,6 @@ swift_library(
|
||||
"//submodules/InstantPageCache:InstantPageCache",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/WalletUI:WalletUI",
|
||||
"//submodules/Markdown:Markdown",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
"//submodules/DeleteChatPeerActionSheetItem:DeleteChatPeerActionSheetItem",
|
||||
|
@ -435,7 +435,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
presentationState = .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating))
|
||||
case let .requesting(ringing):
|
||||
presentationState = .requesting(ringing)
|
||||
case let .active(_, _, keyVisualHash, _, _, _):
|
||||
case let .active(_, _, keyVisualHash, _, _, _, _):
|
||||
self.callWasActive = true
|
||||
if let callContextState = callContextState {
|
||||
switch callContextState {
|
||||
@ -473,12 +473,12 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
if let _ = audioSessionControl {
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
}
|
||||
case let .active(id, key, _, connections, maxLayer, allowsP2P):
|
||||
case let .active(id, key, _, connections, maxLayer, version, allowsP2P):
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
||||
let logName = "\(id.id)_\(id.accessHash)"
|
||||
|
||||
let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, connections: connections, maxLayer: maxLayer, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName)
|
||||
let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName)
|
||||
self.ongoingContext = ongoingContext
|
||||
|
||||
self.debugInfoValue.set(ongoingContext.debugInfo())
|
||||
|
@ -76,7 +76,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
}
|
||||
|
||||
public static var voipVersions: [String] {
|
||||
return [OngoingCallContext.version]
|
||||
return OngoingCallContext.versions
|
||||
}
|
||||
|
||||
public init(accountManager: AccountManager, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) {
|
||||
|
@ -90,13 +90,13 @@ public struct CallId: Equatable {
|
||||
}
|
||||
|
||||
enum CallSessionInternalState {
|
||||
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data)
|
||||
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String])
|
||||
case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, disposable: Disposable)
|
||||
case awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig)
|
||||
case requesting(a: Data, disposable: Disposable)
|
||||
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?)
|
||||
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable)
|
||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, allowsP2P: Bool)
|
||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool)
|
||||
case dropping(Disposable)
|
||||
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
||||
}
|
||||
@ -139,7 +139,7 @@ public enum CallSessionState {
|
||||
case ringing
|
||||
case accepting
|
||||
case requesting(ringing: Bool)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, allowsP2P: Bool)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool)
|
||||
case dropping
|
||||
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
||||
|
||||
@ -155,8 +155,8 @@ public enum CallSessionState {
|
||||
self = .requesting(ringing: true)
|
||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, allowsP2P):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, allowsP2P: allowsP2P)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, allowsP2P):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, allowsP2P: allowsP2P)
|
||||
case .dropping:
|
||||
self = .dropping
|
||||
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
||||
@ -235,6 +235,15 @@ private final class CallSessionContext {
|
||||
}
|
||||
}
|
||||
|
||||
private func selectVersionOnAccept(localVersions: [String], remoteVersions: [String]) -> [String]? {
|
||||
let filteredVersions = localVersions.filter(remoteVersions.contains)
|
||||
if filteredVersions.isEmpty {
|
||||
return nil
|
||||
} else {
|
||||
return [filteredVersions[0]]
|
||||
}
|
||||
}
|
||||
|
||||
private final class CallSessionManagerContext {
|
||||
private let queue: Queue
|
||||
private let postbox: Postbox
|
||||
@ -254,7 +263,7 @@ private final class CallSessionManagerContext {
|
||||
self.postbox = postbox
|
||||
self.network = network
|
||||
self.maxLayer = maxLayer
|
||||
self.versions = versions
|
||||
self.versions = versions.reversed()
|
||||
self.addUpdates = addUpdates
|
||||
}
|
||||
|
||||
@ -338,7 +347,7 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
|
||||
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data) -> CallSessionInternalId? {
|
||||
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String]) -> CallSessionInternalId? {
|
||||
if self.contextIdByStableId[stableId] != nil {
|
||||
return nil
|
||||
}
|
||||
@ -349,7 +358,7 @@ private final class CallSessionManagerContext {
|
||||
|
||||
if randomStatus == 0 {
|
||||
let internalId = CallSessionInternalId()
|
||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b))
|
||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions))
|
||||
self.contexts[internalId] = context
|
||||
let queue = self.queue
|
||||
context.acknowledgeIncomingCallDisposable.set(self.network.request(Api.functions.phone.receivedCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash))).start(error: { [weak self] _ in
|
||||
@ -374,7 +383,7 @@ private final class CallSessionManagerContext {
|
||||
var dropData: (CallSessionStableId, Int64, DropCallSessionReason)?
|
||||
var wasRinging = false
|
||||
switch context.state {
|
||||
case let .ringing(id, accessHash, _, _):
|
||||
case let .ringing(id, accessHash, _, _, _):
|
||||
wasRinging = true
|
||||
let internalReason: DropCallSessionReason
|
||||
switch reason {
|
||||
@ -389,7 +398,7 @@ private final class CallSessionManagerContext {
|
||||
case let .accepting(id, accessHash, _, _, disposable):
|
||||
dropData = (id, accessHash, .abort)
|
||||
disposable.dispose()
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _):
|
||||
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
||||
let internalReason: DropCallSessionReason
|
||||
switch reason {
|
||||
@ -477,8 +486,13 @@ private final class CallSessionManagerContext {
|
||||
func accept(internalId: CallSessionInternalId) {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .ringing(id, accessHash, gAHash, b):
|
||||
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: self.versions) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||
case let .ringing(id, accessHash, gAHash, b, remoteVersions):
|
||||
guard var acceptVersions = selectVersionOnAccept(localVersions: self.versions, remoteVersions: remoteVersions) else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
return
|
||||
}
|
||||
acceptVersions = self.versions
|
||||
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||
if case .accepting = context.state {
|
||||
switch result {
|
||||
@ -489,9 +503,9 @@ private final class CallSessionManagerContext {
|
||||
case let .waiting(config):
|
||||
context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
case let .call(config, gA, timestamp, connections, maxLayer, allowsP2P):
|
||||
case let .call(config, gA, timestamp, connections, maxLayer, version, allowsP2P):
|
||||
if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) {
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, allowsP2P: allowsP2P)
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, allowsP2P: allowsP2P)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@ -515,8 +529,18 @@ private final class CallSessionManagerContext {
|
||||
switch call {
|
||||
case .phoneCallEmpty:
|
||||
break
|
||||
case let .phoneCallAccepted(flags, id, _, _, _, _, gB, _):
|
||||
case let .phoneCallAccepted(flags, id, _, _, _, _, gB, remoteProtocol):
|
||||
let remoteVersions: [String]
|
||||
switch remoteProtocol {
|
||||
case let .phoneCallProtocol(_, _, _, versions):
|
||||
remoteVersions = versions
|
||||
}
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
guard let selectedVersions = selectVersionOnAccept(localVersions: self.versions, remoteVersions: remoteVersions) else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
return
|
||||
}
|
||||
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .requested(_, accessHash, a, gA, config, _):
|
||||
@ -543,7 +567,7 @@ private final class CallSessionManagerContext {
|
||||
|
||||
let keyVisualHash = MTSha256(key + gA)!
|
||||
|
||||
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: self.versions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
||||
if let updatedCall = updatedCall {
|
||||
strongSelf.updateSession(updatedCall, completion: { _ in })
|
||||
@ -586,7 +610,7 @@ private final class CallSessionManagerContext {
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||
@ -603,7 +627,7 @@ private final class CallSessionManagerContext {
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .ringing(id, accessHash, _, _):
|
||||
case let .ringing(id, accessHash, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.ringingStatesUpdated()
|
||||
self.contextUpdated(internalId: internalId)
|
||||
@ -625,9 +649,13 @@ private final class CallSessionManagerContext {
|
||||
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
||||
if keyFingerprint == calculatedKeyId {
|
||||
switch callProtocol {
|
||||
case let .phoneCallProtocol(_, _, maxLayer, _):
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, allowsP2P: allowsP2P)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||
if !versions.isEmpty {
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: allowsP2P)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@ -637,18 +665,27 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _):
|
||||
switch callProtocol {
|
||||
case let .phoneCallProtocol(_, _, maxLayer, _):
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, allowsP2P: allowsP2P)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||
if !versions.isEmpty {
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: allowsP2P)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, _):
|
||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol):
|
||||
let versions: [String]
|
||||
switch requestedProtocol {
|
||||
case let .phoneCallProtocol(_, _, _, libraryVersions):
|
||||
versions = libraryVersions
|
||||
}
|
||||
if self.contextIdByStableId[id] == nil {
|
||||
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData())
|
||||
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions)
|
||||
if let internalId = internalId {
|
||||
var resultRingingStateValue: CallSessionRingingState?
|
||||
for ringingState in self.ringingStatesValue() {
|
||||
@ -847,7 +884,7 @@ public final class CallSessionManager {
|
||||
|
||||
private enum AcceptedCall {
|
||||
case waiting(config: SecretChatEncryptionConfig)
|
||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, allowsP2P: Bool)
|
||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool)
|
||||
}
|
||||
|
||||
private enum AcceptCallResult {
|
||||
@ -896,8 +933,12 @@ private func acceptCallSession(postbox: Postbox, network: Network, stableId: Cal
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate):
|
||||
if id == stableId {
|
||||
switch callProtocol{
|
||||
case let .phoneCallProtocol(_, _, maxLayer, _):
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, allowsP2P: (flags & (1 << 5)) != 0))
|
||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||
if !versions.isEmpty {
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: (flags & (1 << 5)) != 0))
|
||||
} else {
|
||||
return .failed
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .failed
|
||||
|
@ -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> {
|
||||
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 }
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ swift_library(
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
"//submodules/TgVoip:TgVoip",
|
||||
"//submodules/lottie-ios:Lottie",
|
||||
"//submodules/FFMpegBinding:FFMpegBinding",
|
||||
"//submodules/WebPBinding:WebPBinding",
|
||||
@ -185,8 +184,6 @@ swift_library(
|
||||
"//submodules/MessageReactionListUI:MessageReactionListUI",
|
||||
"//submodules/SegmentedControlNode:SegmentedControlNode",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/WalletUI:WalletUI",
|
||||
"//submodules/WalletCore:WalletCore",
|
||||
"//submodules/Markdown:Markdown",
|
||||
"//submodules/SearchPeerMembers:SearchPeerMembers",
|
||||
"//submodules/WidgetItems:WidgetItems",
|
||||
|
@ -13,6 +13,7 @@ swift_library(
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||
"//submodules/TgVoip:TgVoip",
|
||||
"//submodules/TgVoipWebrtc:TgVoipWebrtc",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -4,12 +4,18 @@ import TelegramCore
|
||||
import SyncCore
|
||||
import Postbox
|
||||
import TelegramUIPreferences
|
||||
|
||||
import TgVoip
|
||||
import TgVoipWebrtc
|
||||
|
||||
private func callConnectionDescription(_ connection: CallSessionConnection) -> OngoingCallConnectionDescription {
|
||||
return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag)
|
||||
}
|
||||
|
||||
private func callConnectionDescriptionWebrtc(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtc {
|
||||
return OngoingCallConnectionDescriptionWebrtc(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag)
|
||||
}
|
||||
|
||||
private let callLogsLimit = 20
|
||||
|
||||
public func callLogNameForId(id: Int64, account: Account) -> String? {
|
||||
@ -68,6 +74,11 @@ private let setupLogs: Bool = {
|
||||
Logger.shared.log("TGVOIP", value)
|
||||
}
|
||||
})
|
||||
OngoingCallThreadLocalContextWebrtc.setupLoggingFunction({ value in
|
||||
if let value = value {
|
||||
Logger.shared.log("TGVOIP", value)
|
||||
}
|
||||
})
|
||||
return true
|
||||
}()
|
||||
|
||||
@ -98,6 +109,26 @@ private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCal
|
||||
}
|
||||
}
|
||||
|
||||
private final class OngoingCallThreadLocalContextQueueWebrtcImpl: NSObject, OngoingCallThreadLocalContextQueueWebrtc {
|
||||
private let queue: Queue
|
||||
|
||||
init(queue: Queue) {
|
||||
self.queue = queue
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
func dispatch(_ f: @escaping () -> Void) {
|
||||
self.queue.async {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
func isCurrent() -> Bool {
|
||||
return self.queue.isCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetworkType {
|
||||
switch type {
|
||||
case .none:
|
||||
@ -118,6 +149,26 @@ private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetwor
|
||||
}
|
||||
}
|
||||
|
||||
private func ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtc {
|
||||
switch type {
|
||||
case .none:
|
||||
return .wifi
|
||||
case .wifi:
|
||||
return .wifi
|
||||
case let .cellular(cellular):
|
||||
switch cellular {
|
||||
case .edge:
|
||||
return .cellularEdge
|
||||
case .gprs:
|
||||
return .cellularGprs
|
||||
case .thirdG, .unknown:
|
||||
return .cellular3g
|
||||
case .lte:
|
||||
return .cellularLte
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCallDataSaving {
|
||||
switch type {
|
||||
case .never:
|
||||
@ -131,6 +182,122 @@ private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCal
|
||||
}
|
||||
}
|
||||
|
||||
private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtc {
|
||||
switch type {
|
||||
case .never:
|
||||
return .never
|
||||
case .cellular:
|
||||
return .cellular
|
||||
case .always:
|
||||
return .always
|
||||
default:
|
||||
return .never
|
||||
}
|
||||
}
|
||||
|
||||
private protocol OngoingCallThreadLocalContextProtocol: class {
|
||||
func nativeSetNetworkType(_ type: NetworkType)
|
||||
func nativeSetIsMuted(_ value: Bool)
|
||||
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void)
|
||||
func nativeDebugInfo() -> String
|
||||
func nativeVersion() -> String
|
||||
func nativeGetDerivedState() -> Data
|
||||
}
|
||||
|
||||
private final class OngoingCallThreadLocalContextHolder {
|
||||
let context: OngoingCallThreadLocalContextProtocol
|
||||
|
||||
init(_ context: OngoingCallThreadLocalContextProtocol) {
|
||||
self.context = context
|
||||
}
|
||||
}
|
||||
|
||||
extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
|
||||
func nativeSetNetworkType(_ type: NetworkType) {
|
||||
self.setNetworkType(ongoingNetworkTypeForType(type))
|
||||
}
|
||||
|
||||
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void) {
|
||||
self.stop(completion)
|
||||
}
|
||||
|
||||
func nativeSetIsMuted(_ value: Bool) {
|
||||
self.setIsMuted(value)
|
||||
}
|
||||
|
||||
func nativeDebugInfo() -> String {
|
||||
return self.debugInfo() ?? ""
|
||||
}
|
||||
|
||||
func nativeVersion() -> String {
|
||||
return self.version() ?? ""
|
||||
}
|
||||
|
||||
func nativeGetDerivedState() -> Data {
|
||||
return self.getDerivedState()
|
||||
}
|
||||
}
|
||||
|
||||
extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProtocol {
|
||||
func nativeSetNetworkType(_ type: NetworkType) {
|
||||
self.setNetworkType(ongoingNetworkTypeForTypeWebrtc(type))
|
||||
}
|
||||
|
||||
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void) {
|
||||
self.stop(completion)
|
||||
}
|
||||
|
||||
func nativeSetIsMuted(_ value: Bool) {
|
||||
self.setIsMuted(value)
|
||||
}
|
||||
|
||||
func nativeDebugInfo() -> String {
|
||||
return self.debugInfo() ?? ""
|
||||
}
|
||||
|
||||
func nativeVersion() -> String {
|
||||
return self.version() ?? ""
|
||||
}
|
||||
|
||||
func nativeGetDerivedState() -> Data {
|
||||
return self.getDerivedState()
|
||||
}
|
||||
}
|
||||
|
||||
private extension OngoingCallContextState {
|
||||
init(_ state: OngoingCallState) {
|
||||
switch state {
|
||||
case .initializing:
|
||||
self = .initializing
|
||||
case .connected:
|
||||
self = .connected
|
||||
case .failed:
|
||||
self = .failed
|
||||
case .reconnecting:
|
||||
self = .reconnecting
|
||||
default:
|
||||
self = .failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension OngoingCallContextState {
|
||||
init(_ state: OngoingCallStateWebrtc) {
|
||||
switch state {
|
||||
case .initializing:
|
||||
self = .initializing
|
||||
case .connected:
|
||||
self = .connected
|
||||
case .failed:
|
||||
self = .failed
|
||||
case .reconnecting:
|
||||
self = .reconnecting
|
||||
default:
|
||||
self = .failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class OngoingCallContext {
|
||||
public let internalId: CallSessionInternalId
|
||||
|
||||
@ -138,27 +305,11 @@ public final class OngoingCallContext {
|
||||
private let account: Account
|
||||
private let callSessionManager: CallSessionManager
|
||||
|
||||
private var contextRef: Unmanaged<OngoingCallThreadLocalContext>?
|
||||
private var contextRef: Unmanaged<OngoingCallThreadLocalContextHolder>?
|
||||
|
||||
private let contextState = Promise<OngoingCallState?>(nil)
|
||||
private let contextState = Promise<OngoingCallContextState?>(nil)
|
||||
public var state: Signal<OngoingCallContextState?, NoError> {
|
||||
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)
|
||||
@ -170,16 +321,17 @@ public final class OngoingCallContext {
|
||||
private var networkTypeDisposable: Disposable?
|
||||
|
||||
public static var maxLayer: Int32 {
|
||||
return OngoingCallThreadLocalContext.maxLayer()
|
||||
return max(OngoingCallThreadLocalContext.maxLayer(), OngoingCallThreadLocalContextWebrtc.maxLayer())
|
||||
}
|
||||
|
||||
public static var version: String {
|
||||
return OngoingCallThreadLocalContext.version()
|
||||
public static var versions: [String] {
|
||||
return [OngoingCallThreadLocalContext.version(), OngoingCallThreadLocalContextWebrtc.version()]
|
||||
}
|
||||
|
||||
public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<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
|
||||
OngoingCallThreadLocalContext.applyServerConfig(serializedData)
|
||||
OngoingCallThreadLocalContextWebrtc.applyServerConfig(serializedData)
|
||||
|
||||
self.internalId = internalId
|
||||
self.account = account
|
||||
@ -195,31 +347,59 @@ public final class OngoingCallContext {
|
||||
|> take(1)
|
||||
|> deliverOn(queue)).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
var voipProxyServer: VoipProxyServer?
|
||||
if let proxyServer = proxyServer {
|
||||
switch proxyServer.connection {
|
||||
case let .socks5(username, password):
|
||||
voipProxyServer = VoipProxyServer(host: proxyServer.host, port: proxyServer.port, username: username, password: password)
|
||||
case .mtp:
|
||||
break
|
||||
if version == OngoingCallThreadLocalContextWebrtc.version() {
|
||||
var voipProxyServer: VoipProxyServerWebrtc?
|
||||
if let proxyServer = proxyServer {
|
||||
switch proxyServer.connection {
|
||||
case let .socks5(username, password):
|
||||
voipProxyServer = VoipProxyServerWebrtc(host: proxyServer.host, port: proxyServer.port, username: username, password: password)
|
||||
case .mtp:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath)
|
||||
|
||||
strongSelf.contextRef = Unmanaged.passRetained(context)
|
||||
context.stateChanged = { state in
|
||||
self?.contextState.set(.single(state))
|
||||
}
|
||||
context.signalBarsChanged = { signalBars in
|
||||
self?.receptionPromise.set(.single(signalBars))
|
||||
}
|
||||
|
||||
strongSelf.networkTypeDisposable = (updatedNetworkType
|
||||
|> deliverOn(queue)).start(next: { networkType in
|
||||
self?.withContext { context in
|
||||
context.setNetworkType(ongoingNetworkTypeForType(networkType))
|
||||
let context = OngoingCallThreadLocalContextWebrtc(queue: OngoingCallThreadLocalContextQueueWebrtcImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtc(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtc), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath)
|
||||
|
||||
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
||||
context.stateChanged = { state in
|
||||
self?.contextState.set(.single(OngoingCallContextState(state)))
|
||||
}
|
||||
})
|
||||
context.signalBarsChanged = { signalBars in
|
||||
self?.receptionPromise.set(.single(signalBars))
|
||||
}
|
||||
|
||||
strongSelf.networkTypeDisposable = (updatedNetworkType
|
||||
|> deliverOn(queue)).start(next: { networkType in
|
||||
self?.withContext { context in
|
||||
context.nativeSetNetworkType(networkType)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var voipProxyServer: VoipProxyServer?
|
||||
if let proxyServer = proxyServer {
|
||||
switch proxyServer.connection {
|
||||
case let .socks5(username, password):
|
||||
voipProxyServer = VoipProxyServer(host: proxyServer.host, port: proxyServer.port, username: username, password: password)
|
||||
case .mtp:
|
||||
break
|
||||
}
|
||||
}
|
||||
let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath)
|
||||
|
||||
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
||||
context.stateChanged = { state in
|
||||
self?.contextState.set(.single(OngoingCallContextState(state)))
|
||||
}
|
||||
context.signalBarsChanged = { signalBars in
|
||||
self?.receptionPromise.set(.single(signalBars))
|
||||
}
|
||||
|
||||
strongSelf.networkTypeDisposable = (updatedNetworkType
|
||||
|> deliverOn(queue)).start(next: { networkType in
|
||||
self?.withContext { context in
|
||||
context.nativeSetNetworkType(networkType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
@ -234,18 +414,18 @@ public final class OngoingCallContext {
|
||||
self.networkTypeDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func withContext(_ f: @escaping (OngoingCallThreadLocalContext) -> Void) {
|
||||
private func withContext(_ f: @escaping (OngoingCallThreadLocalContextProtocol) -> Void) {
|
||||
self.queue.async {
|
||||
if let contextRef = self.contextRef {
|
||||
let context = contextRef.takeUnretainedValue()
|
||||
f(context)
|
||||
f(context.context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise<String?>) {
|
||||
self.withContext { context in
|
||||
context.stop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
||||
context.nativeStop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
||||
debugLogValue.set(.single(debugLog))
|
||||
let delta = NetworkUsageStatsConnectionsEntry(
|
||||
cellular: NetworkUsageStatsDirectionsEntry(
|
||||
@ -260,7 +440,7 @@ public final class OngoingCallContext {
|
||||
let _ = saveCallDebugLog(network: self.account.network, callId: callId, log: debugLog).start()
|
||||
}
|
||||
}
|
||||
let derivedState = context.getDerivedState()
|
||||
let derivedState = context.nativeGetDerivedState()
|
||||
let _ = updateVoipDerivedStateInteractively(postbox: self.account.postbox, { _ in
|
||||
return VoipDerivedState(data: derivedState)
|
||||
}).start()
|
||||
@ -269,18 +449,16 @@ public final class OngoingCallContext {
|
||||
|
||||
public func setIsMuted(_ value: Bool) {
|
||||
self.withContext { context in
|
||||
context.setIsMuted(value)
|
||||
context.nativeSetIsMuted(value)
|
||||
}
|
||||
}
|
||||
|
||||
public func debugInfo() -> Signal<(String, String), NoError> {
|
||||
let poll = Signal<(String, String), NoError> { subscriber in
|
||||
self.withContext { context in
|
||||
let version = context.version()
|
||||
let debugInfo = context.debugInfo()
|
||||
if let version = version, let debugInfo = debugInfo {
|
||||
subscriber.putNext((version, debugInfo))
|
||||
}
|
||||
let version = context.nativeVersion()
|
||||
let debugInfo = context.nativeDebugInfo()
|
||||
subscriber.putNext((version, debugInfo))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,31 @@ copts_x86 = [
|
||||
"-DWEBRTC_IOS",
|
||||
]
|
||||
|
||||
replace_symbols = [
|
||||
"WebRtcAgc_Process",
|
||||
"rtc_FatalMessage",
|
||||
"WebRtcAgc_UpdateAgcThresholds",
|
||||
"WebRtcAgc_Init",
|
||||
"WebRtcAgc_GetAddFarendError",
|
||||
"WebRtcAgc_ZeroCtrl",
|
||||
"WebRtcAgc_SaturationCtrl",
|
||||
"WebRtcAgc_SpeakerInactiveCtrl",
|
||||
"WebRtcAgc_ProcessAnalog",
|
||||
"WebRtcAgc_set_config",
|
||||
"WebRtcAgc_get_config",
|
||||
"WebRtcAgc_ExpCurve",
|
||||
"WebRtcAgc_Create",
|
||||
"WebRtcAgc_Free",
|
||||
"WebRtcAgc_AddFarend",
|
||||
"WebRtcAgc_VirtualMic",
|
||||
"WebRtcAgc_AddMic",
|
||||
"WebRtcAgc_InitDigital",
|
||||
"WebRtcAgc_AddFarendToDigital",
|
||||
"WebRtcAgc_CalculateGainTable",
|
||||
"WebRtcAgc_InitVad",
|
||||
"WebRtcAgc_ProcessVad",
|
||||
]
|
||||
|
||||
objc_library(
|
||||
name = "TgVoip",
|
||||
enable_modules = True,
|
||||
@ -46,8 +71,11 @@ objc_library(
|
||||
"-I{}/PublicHeaders/TgVoip".format(package_name()),
|
||||
"-I{}/libtgvoip".format(package_name()),
|
||||
"-I{}/libtgvoip/webrtc_dsp".format(package_name()),
|
||||
"-Isubmodules/Opus/Public/opus",
|
||||
"-DTGVOIP_USE_INSTALLED_OPUS",
|
||||
] + select({
|
||||
"-Drtc=rtc1",
|
||||
"-Dwebrtc=webrtc1",
|
||||
] + ["-D{symbol}={symbol}1".format(symbol=symbol) for symbol in replace_symbols] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_armv7": copts_arm,
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": copts_arm,
|
||||
"@build_bazel_rules_apple//apple:ios_x86_64": copts_x86,
|
||||
|
@ -1,22 +1,8 @@
|
||||
|
||||
copts_arm = [
|
||||
"-DTGVOIP_USE_CUSTOM_CRYPTO",
|
||||
"-DWEBRTC_APM_DEBUG_DUMP=0",
|
||||
"-DWEBRTC_POSIX",
|
||||
"-DTGVOIP_HAVE_TGLOG",
|
||||
"-DWEBRTC_NS_FLOAT",
|
||||
"-DWEBRTC_IOS",
|
||||
"-DWEBRTC_HAS_NEON",
|
||||
]
|
||||
|
||||
copts_x86 = [
|
||||
"-DTGVOIP_USE_CUSTOM_CRYPTO",
|
||||
"-DWEBRTC_APM_DEBUG_DUMP=0",
|
||||
"-DWEBRTC_POSIX",
|
||||
"-DTGVOIP_HAVE_TGLOG",
|
||||
"-DWEBRTC_NS_FLOAT",
|
||||
"-DWEBRTC_IOS",
|
||||
]
|
||||
cc_library(
|
||||
name = "webrtc_lib",
|
||||
srcs = ["libwebrtc.a"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "TgVoipWebrtc",
|
||||
@ -26,38 +12,28 @@ objc_library(
|
||||
"Sources/**/*.m",
|
||||
"Sources/**/*.mm",
|
||||
"Sources/**/*.h",
|
||||
"libtgvoip/*.m",
|
||||
"libtgvoip/*.mm",
|
||||
"libtgvoip/*.cpp",
|
||||
"libtgvoip/audio/*.cpp",
|
||||
"libtgvoip/video/*.cpp",
|
||||
"libtgvoip/os/darwin/*.m",
|
||||
"libtgvoip/os/darwin/*.mm",
|
||||
"libtgvoip/os/darwin/*.cpp",
|
||||
"libtgvoip/os/posix/*.cpp",
|
||||
"libtgvoip/webrtc_dsp/**/*.c",
|
||||
"libtgvoip/webrtc_dsp/**/*.cc",
|
||||
"libtgvoip/webrtc_dsp/**/*.cpp",
|
||||
], exclude = ["libtgvoip/os/darwin/*OSX*"]),
|
||||
"Impl/*.h",
|
||||
"Impl/*.cpp",
|
||||
]),
|
||||
hdrs = glob([
|
||||
"PublicHeaders/**/*.h",
|
||||
]),
|
||||
copts = [
|
||||
"-I{}/PublicHeaders/TgVoip".format(package_name()),
|
||||
"-I{}/libtgvoip".format(package_name()),
|
||||
"-I{}/libtgvoip/webrtc_dsp".format(package_name()),
|
||||
"-DTGVOIP_USE_INSTALLED_OPUS",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_armv7": copts_arm,
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": copts_arm,
|
||||
"@build_bazel_rules_apple//apple:ios_x86_64": copts_x86,
|
||||
}),
|
||||
"-I{}/Impl".format(package_name()),
|
||||
"-Ithird-party/webrtc/webrtc-ios/src".format(package_name()),
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/third_party/abseil-cpp".format(package_name()),
|
||||
"-DWEBRTC_IOS",
|
||||
"-DWEBRTC_MAC",
|
||||
"-DWEBRTC_POSIX",
|
||||
],
|
||||
includes = [
|
||||
"PublicHeaders",
|
||||
],
|
||||
deps = [
|
||||
"//third-party/webrtc:webrtc_lib",
|
||||
"//submodules/MtProtoKit:MtProtoKit",
|
||||
"//submodules/Opus:opus",
|
||||
"//submodules/openssl:openssl",
|
||||
],
|
||||
sdk_frameworks = [
|
||||
"Foundation",
|
||||
|
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>
|
||||
|
||||
@interface OngoingCallConnectionDescription : NSObject
|
||||
@interface OngoingCallConnectionDescriptionWebrtc : NSObject
|
||||
|
||||
@property (nonatomic, readonly) int64_t connectionId;
|
||||
@property (nonatomic, strong, readonly) NSString * _Nonnull ip;
|
||||
@ -15,14 +15,14 @@
|
||||
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(int32_t, OngoingCallState) {
|
||||
typedef NS_ENUM(int32_t, OngoingCallStateWebrtc) {
|
||||
OngoingCallStateInitializing,
|
||||
OngoingCallStateConnected,
|
||||
OngoingCallStateFailed,
|
||||
OngoingCallStateReconnecting
|
||||
};
|
||||
|
||||
typedef NS_ENUM(int32_t, OngoingCallNetworkType) {
|
||||
typedef NS_ENUM(int32_t, OngoingCallNetworkTypeWebrtc) {
|
||||
OngoingCallNetworkTypeWifi,
|
||||
OngoingCallNetworkTypeCellularGprs,
|
||||
OngoingCallNetworkTypeCellularEdge,
|
||||
@ -30,20 +30,20 @@ typedef NS_ENUM(int32_t, OngoingCallNetworkType) {
|
||||
OngoingCallNetworkTypeCellularLte
|
||||
};
|
||||
|
||||
typedef NS_ENUM(int32_t, OngoingCallDataSaving) {
|
||||
typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
||||
OngoingCallDataSavingNever,
|
||||
OngoingCallDataSavingCellular,
|
||||
OngoingCallDataSavingAlways
|
||||
};
|
||||
|
||||
@protocol OngoingCallThreadLocalContextQueue <NSObject>
|
||||
@protocol OngoingCallThreadLocalContextQueueWebrtc <NSObject>
|
||||
|
||||
- (void)dispatch:(void (^ _Nonnull)())f;
|
||||
- (bool)isCurrent;
|
||||
|
||||
@end
|
||||
|
||||
@interface VoipProxyServer : NSObject
|
||||
@interface VoipProxyServerWebrtc : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSString * _Nonnull host;
|
||||
@property (nonatomic, readonly) int32_t port;
|
||||
@ -54,18 +54,18 @@ typedef NS_ENUM(int32_t, OngoingCallDataSaving) {
|
||||
|
||||
@end
|
||||
|
||||
@interface OngoingCallThreadLocalContext : NSObject
|
||||
@interface OngoingCallThreadLocalContextWebrtc : NSObject
|
||||
|
||||
+ (void)setupLoggingFunction:(void (* _Nullable)(NSString * _Nullable))loggingFunction;
|
||||
+ (void)applyServerConfig:(NSString * _Nullable)data;
|
||||
+ (int32_t)maxLayer;
|
||||
+ (NSString * _Nonnull)version;
|
||||
|
||||
@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallState);
|
||||
@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc);
|
||||
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
|
||||
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueue> _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescription *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath;
|
||||
- (void)stop:(void (^_Nonnull)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
|
||||
- (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 (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
|
||||
|
||||
- (bool)needRate;
|
||||
|
||||
@ -74,7 +74,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSaving) {
|
||||
- (NSData * _Nonnull)getDerivedState;
|
||||
|
||||
- (void)setIsMuted:(bool)isMuted;
|
||||
- (void)setNetworkType:(OngoingCallNetworkType)networkType;
|
||||
- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -1,44 +1,10 @@
|
||||
#import "OngoingCallThreadLocalContext.h"
|
||||
#import "TgVoip/OngoingCallThreadLocalContext.h"
|
||||
|
||||
#import "TgVoip.h"
|
||||
|
||||
#import <MtProtoKit/MtProtoKit.h>
|
||||
#include <memory>
|
||||
using namespace TGVOIP_NAMESPACE;
|
||||
|
||||
static void TGCallAesIgeEncrypt(uint8_t *inBytes, uint8_t *outBytes, size_t length, uint8_t *key, uint8_t *iv) {
|
||||
MTAesEncryptRaw(inBytes, outBytes, length, key, iv);
|
||||
}
|
||||
|
||||
static void TGCallAesIgeDecrypt(uint8_t *inBytes, uint8_t *outBytes, size_t length, uint8_t *key, uint8_t *iv) {
|
||||
MTAesDecryptRaw(inBytes, outBytes, length, key, iv);
|
||||
}
|
||||
|
||||
static void TGCallSha1(uint8_t *msg, size_t length, uint8_t *output) {
|
||||
MTRawSha1(msg, length, output);
|
||||
}
|
||||
|
||||
static void TGCallSha256(uint8_t *msg, size_t length, uint8_t *output) {
|
||||
MTRawSha256(msg, length, output);
|
||||
}
|
||||
|
||||
static void TGCallAesCtrEncrypt(uint8_t *inOut, size_t length, uint8_t *key, uint8_t *iv, uint8_t *ecount, uint32_t *num) {
|
||||
uint8_t *outData = (uint8_t *)malloc(length);
|
||||
MTAesCtr *aesCtr = [[MTAesCtr alloc] initWithKey:key keyLength:32 iv:iv ecount:ecount num:*num];
|
||||
[aesCtr encryptIn:inOut out:outData len:length];
|
||||
memcpy(inOut, outData, length);
|
||||
free(outData);
|
||||
|
||||
[aesCtr getIv:iv];
|
||||
|
||||
memcpy(ecount, [aesCtr ecount], 16);
|
||||
*num = [aesCtr num];
|
||||
}
|
||||
|
||||
static void TGCallRandomBytes(uint8_t *buffer, size_t length) {
|
||||
arc4random_buf(buffer, length);
|
||||
}
|
||||
|
||||
@implementation OngoingCallConnectionDescription
|
||||
@implementation OngoingCallConnectionDescriptionWebrtc
|
||||
|
||||
- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag {
|
||||
self = [super init];
|
||||
@ -54,74 +20,11 @@ static void TGCallRandomBytes(uint8_t *buffer, size_t length) {
|
||||
|
||||
@end
|
||||
|
||||
static MTAtomic *callContexts() {
|
||||
static MTAtomic *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[MTAtomic alloc] initWithValue:[[NSMutableDictionary alloc] init]];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
@interface OngoingCallThreadLocalContextReference : NSObject
|
||||
|
||||
@property (nonatomic, weak) OngoingCallThreadLocalContext *context;
|
||||
@property (nonatomic, strong, readonly) id<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;
|
||||
@interface OngoingCallThreadLocalContextWebrtc () {
|
||||
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
|
||||
int32_t _contextId;
|
||||
|
||||
OngoingCallNetworkType _networkType;
|
||||
OngoingCallNetworkTypeWebrtc _networkType;
|
||||
NSTimeInterval _callReceiveTimeout;
|
||||
NSTimeInterval _callRingTimeout;
|
||||
NSTimeInterval _callConnectTimeout;
|
||||
@ -129,7 +32,7 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte
|
||||
|
||||
TgVoip *_tgVoip;
|
||||
|
||||
OngoingCallState _state;
|
||||
OngoingCallStateWebrtc _state;
|
||||
int32_t _signalBars;
|
||||
NSData *_lastDerivedState;
|
||||
}
|
||||
@ -139,7 +42,7 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte
|
||||
|
||||
@end
|
||||
|
||||
@implementation VoipProxyServer
|
||||
@implementation VoipProxyServerWebrtc
|
||||
|
||||
- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password {
|
||||
self = [super init];
|
||||
@ -154,7 +57,7 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte
|
||||
|
||||
@end
|
||||
|
||||
static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkType type) {
|
||||
static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkTypeWebrtc type) {
|
||||
switch (type) {
|
||||
case OngoingCallNetworkTypeWifi:
|
||||
return TgVoipNetworkType::WiFi;
|
||||
@ -169,7 +72,7 @@ static TgVoipNetworkType callControllerNetworkTypeForType(OngoingCallNetworkType
|
||||
}
|
||||
}
|
||||
|
||||
static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSaving type) {
|
||||
static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSavingWebrtc type) {
|
||||
switch (type) {
|
||||
case OngoingCallDataSavingNever:
|
||||
return TgVoipDataSaving::Never;
|
||||
@ -182,7 +85,7 @@ static TgVoipDataSaving callControllerDataSavingForType(OngoingCallDataSaving ty
|
||||
}
|
||||
}
|
||||
|
||||
@implementation OngoingCallThreadLocalContext
|
||||
@implementation OngoingCallThreadLocalContextWebrtc
|
||||
|
||||
static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
|
||||
@ -206,15 +109,14 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
}
|
||||
|
||||
+ (NSString *)version {
|
||||
return [NSString stringWithUTF8String:TgVoip::getVersion().c_str()];
|
||||
return @"2.7.7";
|
||||
}
|
||||
|
||||
- (instancetype _Nonnull)initWithQueue:(id<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];
|
||||
if (self != nil) {
|
||||
_queue = queue;
|
||||
assert([queue isCurrent]);
|
||||
_contextId = addContext(self, queue);
|
||||
|
||||
_callReceiveTimeout = 20.0;
|
||||
_callRingTimeout = 90.0;
|
||||
@ -236,17 +138,17 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
proxyValue = std::unique_ptr<TgVoipProxy>(proxyObject);
|
||||
}
|
||||
|
||||
TgVoipCrypto crypto;
|
||||
/*TgVoipCrypto crypto;
|
||||
crypto.sha1 = &TGCallSha1;
|
||||
crypto.sha256 = &TGCallSha256;
|
||||
crypto.rand_bytes = &TGCallRandomBytes;
|
||||
crypto.aes_ige_encrypt = &TGCallAesIgeEncrypt;
|
||||
crypto.aes_ige_decrypt = &TGCallAesIgeDecrypt;
|
||||
crypto.aes_ctr_encrypt = &TGCallAesCtrEncrypt;
|
||||
crypto.aes_ctr_encrypt = &TGCallAesCtrEncrypt;*/
|
||||
|
||||
std::vector<TgVoipEndpoint> endpoints;
|
||||
NSArray<OngoingCallConnectionDescription *> *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections];
|
||||
for (OngoingCallConnectionDescription *connection in connections) {
|
||||
NSArray<OngoingCallConnectionDescriptionWebrtc *> *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections];
|
||||
for (OngoingCallConnectionDescriptionWebrtc *connection in connections) {
|
||||
unsigned char peerTag[16];
|
||||
[connection.peerTag getBytes:peerTag length:16];
|
||||
|
||||
@ -272,7 +174,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
.enableAGC = true,
|
||||
.enableCallUpgrade = false,
|
||||
.logPath = logPath.length == 0 ? "" : std::string(logPath.UTF8String),
|
||||
.maxApiLayer = [OngoingCallThreadLocalContext maxLayer]
|
||||
.maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer]
|
||||
};
|
||||
|
||||
std::vector<uint8_t> encryptionKeyValue;
|
||||
@ -284,40 +186,27 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
.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(
|
||||
config,
|
||||
{ derivedStateValue },
|
||||
endpoints,
|
||||
proxyValue,
|
||||
callControllerNetworkTypeForType(networkType),
|
||||
encryptionKey,
|
||||
crypto
|
||||
encryptionKey
|
||||
);
|
||||
|
||||
_state = OngoingCallStateInitializing;
|
||||
_signalBars = -1;
|
||||
|
||||
__weak OngoingCallThreadLocalContext *weakSelf = self;
|
||||
__weak OngoingCallThreadLocalContextWebrtc *weakSelf = self;
|
||||
_tgVoip->setOnStateUpdated([weakSelf](TgVoipState state) {
|
||||
__strong OngoingCallThreadLocalContext *strongSelf = weakSelf;
|
||||
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
[strongSelf controllerStateChanged:state];
|
||||
}
|
||||
});
|
||||
_tgVoip->setOnSignalBarsUpdated([weakSelf](int signalBars) {
|
||||
__strong OngoingCallThreadLocalContext *strongSelf = weakSelf;
|
||||
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
[strongSelf signalBarsChanged:signalBars];
|
||||
}
|
||||
@ -328,12 +217,15 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
|
||||
- (void)dealloc {
|
||||
assert([_queue isCurrent]);
|
||||
removeContext(_contextId);
|
||||
if (_tgVoip != NULL) {
|
||||
[self stop:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (bool)needRate {
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
|
||||
if (_tgVoip) {
|
||||
TgVoipFinalState finalState = _tgVoip->stop();
|
||||
@ -352,8 +244,10 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
|
||||
- (NSString *)debugInfo {
|
||||
if (_tgVoip != nil) {
|
||||
auto rawDebugString = _tgVoip->getDebugInfo();
|
||||
return [NSString stringWithUTF8String:rawDebugString.c_str()];
|
||||
NSString *version = [self version];
|
||||
return [NSString stringWithFormat:@"WebRTC, Version: %@", version];
|
||||
//auto rawDebugString = _tgVoip->getDebugInfo();
|
||||
//return [NSString stringWithUTF8String:rawDebugString.c_str()];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
@ -361,7 +255,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
|
||||
- (NSString *)version {
|
||||
if (_tgVoip != nil) {
|
||||
return [NSString stringWithUTF8String:_tgVoip->getVersion().c_str()];
|
||||
return @"2.7.7";//[NSString stringWithUTF8String:_tgVoip->getVersion().c_str()];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
@ -379,7 +273,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
}
|
||||
|
||||
- (void)controllerStateChanged:(TgVoipState)state {
|
||||
OngoingCallState callState = OngoingCallStateInitializing;
|
||||
OngoingCallStateWebrtc callState = OngoingCallStateInitializing;
|
||||
switch (state) {
|
||||
case TgVoipState::Estabilished:
|
||||
callState = OngoingCallStateConnected;
|
||||
@ -419,7 +313,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setNetworkType:(OngoingCallNetworkType)networkType {
|
||||
- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType {
|
||||
if (_networkType != networkType) {
|
||||
_networkType = networkType;
|
||||
if (_tgVoip) {
|
||||
@ -429,3 +323,4 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
@ -39,8 +39,6 @@ swift_library(
|
||||
"//submodules/UrlEscaping:UrlEscaping",
|
||||
"//submodules/LocalAuth:LocalAuth",
|
||||
"//submodules/ScreenCaptureDetection:ScreenCaptureDetection",
|
||||
"//submodules/WalletUrl:WalletUrl",
|
||||
"//submodules/WalletCore:WalletCore",
|
||||
"//submodules/ActivityIndicator:ActivityIndicator",
|
||||
"//submodules/ProgressNavigationButtonNode:ProgressNavigationButtonNode",
|
||||
"//submodules/Markdown:Markdown",
|
||||
|
@ -107,6 +107,7 @@ openssl_headers = [
|
||||
|
||||
openssl_libs = [
|
||||
"libcrypto.a",
|
||||
"libssl.a",
|
||||
]
|
||||
|
||||
genrule(
|
||||
@ -119,6 +120,7 @@ genrule(
|
||||
],
|
||||
cmd_bash =
|
||||
"""
|
||||
|
||||
if [ "$(TARGET_CPU)" == "ios_armv7" ]; then
|
||||
BUILD_ARCH="armv7"
|
||||
elif [ "$(TARGET_CPU)" == "ios_arm64" ]; then
|
||||
|
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