This commit is contained in:
Ali 2020-05-05 18:00:02 +04:00
parent 16539d5f69
commit 763876773c
43 changed files with 3170 additions and 289 deletions

6
.gitmodules vendored
View File

@ -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

View File

@ -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",

View File

@ -17,7 +17,6 @@ swift_library(
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/SyncCore:SyncCore",
"//submodules/WalletCore:WalletCore",
],
visibility = [
"//visibility:public",

View File

@ -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",

View File

@ -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())

View File

@ -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>) {

View File

@ -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

View File

@ -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 }
}

View File

@ -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",

View File

@ -13,6 +13,7 @@ swift_library(
"//submodules/Postbox:Postbox",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/TgVoip:TgVoip",
"//submodules/TgVoipWebrtc:TgVoipWebrtc",
],
visibility = [
"//visibility:public",

View File

@ -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()
}

View File

@ -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,

View File

@ -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",

View 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();
}

View 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

View 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);
}

View 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

View 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);
}

View 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

View 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());
}

View 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

View 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)) {}

View 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

View 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

View 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;
}

View 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

View 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

View 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;
}

View 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

View 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) {}

View 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

View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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
View File

@ -0,0 +1,8 @@
filegroup(
name = "depot_tools_sources",
srcs = glob([
"depot_tools/**/*"
]),
visibility = ["//visibility:public"],
)

1
third-party/depot_tools vendored Submodule

@ -0,0 +1 @@
Subproject commit ae7f4c51114152e95cf1e9b6d2f0a4936fb7c5d6

78
third-party/webrtc/BUILD vendored Normal file
View 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
View 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
View 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

@ -0,0 +1 @@
Subproject commit 6774acc02feb905ee8209a2e187c8cc464c87289