Conference calls

This commit is contained in:
Isaac
2025-03-30 02:06:50 +04:00
parent 66c244f540
commit aaf52d4282
1585 changed files with 712275 additions and 1047 deletions

View File

@@ -17,6 +17,7 @@ import DeviceProximity
import UndoUI
import TemporaryCachedPeerDataManager
import CallsEmoji
import TdBinding
private extension GroupCallParticipantsContext.Participant {
var allSsrcs: Set<UInt32> {
@@ -94,7 +95,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
public init(account: Account, engine: TelegramEngine, peerId: PeerId?, isChannel: Bool, call: EngineGroupCallDescription) {
self.panelDataPromise.set(.single(nil))
let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil)
let state = engine.calls.getGroupCallParticipants(reference: .id(id: call.id, accessHash: call.accessHash), offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|> map(Optional.init)
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
return .single(nil)
@@ -118,7 +119,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
peerId: peerId,
myPeerId: account.peerId,
id: call.id,
accessHash: call.accessHash,
reference: .id(id: call.id, accessHash: call.accessHash),
state: state,
previousServiceState: nil
)
@@ -147,7 +148,21 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
return GroupCallPanelData(
peerId: peerId,
isChannel: isChannel,
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit, isStream: state.isStream, upgradedPrivateCallId: state.upgradedPrivateCallId),
info: GroupCallInfo(
id: call.id,
accessHash: call.accessHash,
participantCount: state.totalCount,
streamDcId: nil,
title: state.title,
scheduleTimestamp: state.scheduleTimestamp,
subscribedToScheduled: state.subscribedToScheduled,
recordingStartTimestamp: nil,
sortAscending: state.sortAscending,
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
isVideoEnabled: state.isVideoEnabled,
unmutedVideoLimit: state.unmutedVideoLimit,
isStream: state.isStream
),
topParticipants: topParticipants,
participantCount: state.totalCount,
activeSpeakers: activeSpeakers,
@@ -537,11 +552,11 @@ private final class ScreencastInProcessIPCContext: ScreencastIPCContext {
preferX264: false,
logPath: "",
onMutedSpeechActivityDetected: { _ in },
encryptionKey: nil,
isConference: self.isConference,
audioIsActiveByDefault: true,
isStream: false,
sharedAudioDevice: nil
sharedAudioDevice: nil,
encryptionContext: nil
)
)
self.screencastCallContext = screencastCallContext
@@ -613,10 +628,12 @@ private final class PendingConferenceInvitationContext {
private var didNotifyEnded: Bool = false
init(callSessionManager: CallSessionManager, groupCall: GroupCallReference, encryptionKey: Data, peerId: PeerId, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) {
init(callSessionManager: CallSessionManager, groupCall: GroupCallReference, peerId: PeerId, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) {
self.callSessionManager = callSessionManager
self.requestDisposable = (callSessionManager.request(peerId: peerId, isVideo: false, enableVideo: true, conferenceCall: (groupCall, encryptionKey))
preconditionFailure()
/*self.requestDisposable = (callSessionManager.request(peerId: peerId, isVideo: false, enableVideo: true, conferenceCall: (groupCall, encryptionKey))
|> deliverOnMainQueue).startStrict(next: { [weak self] internalId in
guard let self else {
return
@@ -642,7 +659,7 @@ private final class PendingConferenceInvitationContext {
break
}
})
})
})*/
}
deinit {
@@ -786,7 +803,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
private(set) var initialCall: EngineGroupCallDescription?
private(set) var initialCall: (description: EngineGroupCallDescription, reference: InternalGroupCallReference)?
public let internalId: CallSessionInternalId
public let peerId: EnginePeer.Id?
private let isChannel: Bool
@@ -795,10 +812,28 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var ignorePreviousJoinAsPeerId: (PeerId, UInt32)?
private var reconnectingAsPeer: EnginePeer?
public private(set) var callId: Int64?
public private(set) var hasVideo: Bool
public private(set) var hasScreencast: Bool
private let isVideoEnabled: Bool
private let keyPair: TelegramKeyPair?
private final class E2ECallState {
var call: TdCall?
var pendingIncomingBroadcastBlocks: [Data] = []
}
private let e2eCall = Atomic<E2ECallState>(value: E2ECallState())
private var e2ePoll0Offset: Int?
private var e2ePoll0Timer: Foundation.Timer?
private var e2ePoll0Disposable: Disposable?
private var e2ePoll1Offset: Int?
private var e2ePoll1Timer: Foundation.Timer?
private var e2ePoll1Disposable: Disposable?
private var temporaryJoinTimestamp: Int32
private var temporaryActivityTimestamp: Double?
private var temporaryActivityRank: Int?
@@ -864,6 +899,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return self.isNoiseSuppressionEnabledPromise.get()
}
private let isNoiseSuppressionEnabledDisposable = MetaDisposable()
private let e2eEncryptionKeyHashValue = ValuePromise<Data?>(nil)
public var e2eEncryptionKeyHash: Signal<Data?, NoError> {
return self.e2eEncryptionKeyHashValue.get()
}
private var isVideoMuted: Bool = false
private let isVideoMutedDisposable = MetaDisposable()
@@ -1053,18 +1093,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var screencastStateDisposable: Disposable?
public let isStream: Bool
private let encryptionKey: (key: Data, fingerprint: Int64)?
private let sharedAudioContext: SharedCallAudioContext?
private let conferenceFromCallId: CallId?
public let isConference: Bool
public var encryptionKeyValue: Data? {
if let key = self.encryptionKey?.key {
return dataForEmojiRawKey(key)
} else {
return nil
}
}
private let conferenceSourceId: CallSessionInternalId?
public var conferenceSource: CallSessionInternalId? {
@@ -1085,15 +1116,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
audioSession: ManagedAudioSession,
callKitIntegration: CallKitIntegration?,
getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void),
initialCall: EngineGroupCallDescription?,
initialCall: (description: EngineGroupCallDescription, reference: InternalGroupCallReference)?,
internalId: CallSessionInternalId,
peerId: EnginePeer.Id?,
isChannel: Bool,
invite: String?,
joinAsPeerId: EnginePeer.Id?,
isStream: Bool,
encryptionKey: (key: Data, fingerprint: Int64)?,
conferenceFromCallId: CallId?,
keyPair: TelegramKeyPair?,
conferenceSourceId: CallSessionInternalId?,
isConference: Bool,
sharedAudioContext: SharedCallAudioContext?
@@ -1105,15 +1135,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.getDeviceAccessData = getDeviceAccessData
self.initialCall = initialCall
self.callId = initialCall?.description.id
self.internalId = internalId
self.peerId = peerId
self.isChannel = isChannel
self.invite = invite
self.joinAsPeerId = joinAsPeerId ?? accountContext.account.peerId
self.schedulePending = initialCall == nil
self.isScheduled = initialCall == nil || initialCall?.scheduleTimestamp != nil
self.isScheduled = initialCall == nil || initialCall?.description.scheduleTimestamp != nil
self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.title, scheduleTimestamp: initialCall?.scheduleTimestamp, subscribedToScheduled: initialCall?.subscribedToScheduled ?? false)
self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.description.title, scheduleTimestamp: initialCall?.description.scheduleTimestamp, subscribedToScheduled: initialCall?.description.subscribedToScheduled ?? false)
self.statePromise = ValuePromise(self.stateValue)
self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
@@ -1122,10 +1154,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.hasVideo = false
self.hasScreencast = false
self.isStream = isStream
self.conferenceFromCallId = conferenceFromCallId
self.conferenceSourceId = conferenceSourceId
self.isConference = isConference
self.encryptionKey = encryptionKey
self.keyPair = keyPair
var sharedAudioContext = sharedAudioContext
if sharedAudioContext == nil {
@@ -1283,14 +1314,47 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
}
} else if case .joined = participantUpdate.participationStatusChange {
} else if let ssrc = participantUpdate.ssrc, self.ssrcMapping[ssrc] == nil {
}
}
case let .call(isTerminated, _, _, _, _, _, _):
if isTerminated {
self.markAsCanBeRemoved()
}
case let .conferenceChainBlocks(subChainId, blocks, nextOffset):
if let _ = self.keyPair {
var processBlock = true
let updateBaseOffset = nextOffset - blocks.count
if subChainId == 0 {
if let e2ePoll0Offset = self.e2ePoll0Offset {
if e2ePoll0Offset == updateBaseOffset {
self.e2ePoll0Offset = nextOffset
} else if e2ePoll0Offset < updateBaseOffset {
self.e2ePoll(subChainId: subChainId)
} else {
processBlock = false
}
} else {
processBlock = false
}
} else if subChainId == 1 {
if let e2ePoll1Offset = self.e2ePoll1Offset {
if e2ePoll1Offset == updateBaseOffset {
self.e2ePoll1Offset = nextOffset
} else if e2ePoll1Offset < updateBaseOffset {
self.e2ePoll(subChainId: subChainId)
} else {
processBlock = false
}
} else {
processBlock = false
}
} else {
processBlock = false
}
if processBlock {
self.addE2EBlocks(blocks: blocks, subChainId: subChainId)
}
}
}
}
}
@@ -1321,7 +1385,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
})
if let initialCall = initialCall, let peerId, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled, isStream: initialCall.isStream))
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.description.id, accessHash: initialCall.description.accessHash, title: initialCall.description.title, scheduleTimestamp: initialCall.description.scheduleTimestamp, subscribedToScheduled: initialCall.description.subscribedToScheduled, isStream: initialCall.description.isStream))
}) {
self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId)
} else {
@@ -1447,6 +1511,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.peerUpdatesSubscription?.dispose()
self.screencastStateDisposable?.dispose()
self.pendingDisconnedUpgradedConferenceCallTimer?.invalidate()
self.e2ePoll0Timer?.invalidate()
self.e2ePoll0Disposable?.dispose()
self.e2ePoll1Timer?.invalidate()
self.e2ePoll1Disposable?.dispose()
}
private func switchToTemporaryParticipantsContext(sourceContext: GroupCallParticipantsContext?, oldMyPeerId: PeerId) {
@@ -1471,7 +1539,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
if let sourceContext = sourceContext, let initialState = sourceContext.immediateState {
let temporaryParticipantsContext = self.accountContext.engine.calls.groupCall(peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, accessHash: sourceContext.accessHash, state: initialState, previousServiceState: sourceContext.serviceState)
let temporaryParticipantsContext = self.accountContext.engine.calls.groupCall(peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, reference: sourceContext.reference, state: initialState, previousServiceState: sourceContext.serviceState)
self.temporaryParticipantsContext = temporaryParticipantsContext
self.participantsContextStateDisposable.set((combineLatest(queue: .mainQueue(),
myPeerData,
@@ -1702,7 +1770,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
peerId: self.peerId,
myPeerId: self.joinAsPeerId,
id: callInfo.id,
accessHash: callInfo.accessHash,
reference: .id(id: callInfo.id, accessHash: callInfo.accessHash),
state: GroupCallParticipantsContext.State(
participants: [],
nextParticipantsFetchOffset: nil,
@@ -1718,7 +1786,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
isVideoEnabled: callInfo.isVideoEnabled,
unmutedVideoLimit: callInfo.unmutedVideoLimit,
isStream: callInfo.isStream,
upgradedPrivateCallId: callInfo.upgradedPrivateCallId,
version: 0
),
previousServiceState: nil
@@ -1817,7 +1884,21 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.stateValue.subscribedToScheduled = state.subscribedToScheduled
self.stateValue.scheduleTimestamp = self.isScheduledStarted ? nil : state.scheduleTimestamp
if state.scheduleTimestamp == nil && !self.isScheduledStarted {
self.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true, defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted, isVideoEnabled: callInfo.isVideoEnabled, unmutedVideoLimit: callInfo.unmutedVideoLimit, isStream: callInfo.isStream, upgradedPrivateCallId: callInfo.upgradedPrivateCallId)), audioSessionControl: self.audioSessionControl)
self.updateSessionState(internalState: .active(GroupCallInfo(
id: callInfo.id,
accessHash: callInfo.accessHash,
participantCount: state.totalCount,
streamDcId: callInfo.streamDcId,
title: state.title,
scheduleTimestamp: nil,
subscribedToScheduled: false,
recordingStartTimestamp: nil,
sortAscending: true,
defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted,
isVideoEnabled: callInfo.isVideoEnabled,
unmutedVideoLimit: callInfo.unmutedVideoLimit,
isStream: callInfo.isStream
)), audioSessionControl: self.audioSessionControl)
} else {
self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
id: callInfo.id,
@@ -1832,8 +1913,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
isVideoEnabled: state.isVideoEnabled,
unmutedVideoLimit: state.unmutedVideoLimit,
isStream: callInfo.isStream,
upgradedPrivateCallId: callInfo.upgradedPrivateCallId
isStream: callInfo.isStream
))))
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
@@ -1929,9 +2009,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
outgoingAudioBitrateKbit = Int32(value)
}
var encryptionKey: Data?
encryptionKey = self.encryptionKey?.key
let contextAudioSessionActive: Signal<Bool, NoError>
if self.sharedAudioContext != nil {
contextAudioSessionActive = .single(true)
@@ -1943,6 +2020,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if self.isConference && self.conferenceSourceId != nil {
audioIsActiveByDefault = false
}
var encryptionContext: OngoingGroupCallEncryptionContext?
if self.isConference {
class OngoingGroupCallEncryptionContextImpl: OngoingGroupCallEncryptionContext {
private let e2eCall: Atomic<E2ECallState>
init(e2eCall: Atomic<E2ECallState>) {
self.e2eCall = e2eCall
}
func encrypt(message: Data) -> Data? {
return self.e2eCall.with({ $0.call?.encrypt(message) })
}
func decrypt(message: Data) -> Data? {
return self.e2eCall.with({ $0.call?.decrypt(message) })
}
}
encryptionContext = OngoingGroupCallEncryptionContextImpl(e2eCall: self.e2eCall)
}
genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: contextAudioSessionActive, video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in
let disposable = MetaDisposable()
@@ -1969,7 +2066,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
self.onMutedSpeechActivityDetected?(value)
}
}, encryptionKey: encryptionKey, isConference: self.isConference, audioIsActiveByDefault: audioIsActiveByDefault, isStream: self.isStream, sharedAudioDevice: self.sharedAudioContext?.audioDevice))
}, isConference: self.isConference, audioIsActiveByDefault: audioIsActiveByDefault, isStream: self.isStream, sharedAudioDevice: self.sharedAudioContext?.audioDevice, encryptionContext: encryptionContext))
}
self.genericCallContext = genericCallContext
@@ -2084,18 +2181,51 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} else {
peerAdminIds = .single([])
}
var generateE2EData: ((Data?) -> JoinGroupCallE2E?)?
if let keyPair = self.keyPair {
if let mappedKeyPair = TdKeyPair(keyId: keyPair.id, publicKey: keyPair.publicKey.data) {
let userId = self.joinAsPeerId.id._internalGetInt64Value()
generateE2EData = { block -> JoinGroupCallE2E? in
if let block {
guard let resultBlock = tdGenerateSelfAddBlock(mappedKeyPair, userId, block) else {
return nil
}
return JoinGroupCallE2E(
publicKey: keyPair.publicKey,
block: resultBlock
)
} else {
guard let resultBlock = tdGenerateZeroBlock(mappedKeyPair, userId) else {
return nil
}
return JoinGroupCallE2E(
publicKey: keyPair.publicKey,
block: resultBlock
)
}
}
}
}
let reference: InternalGroupCallReference
if let initialCall = self.initialCall {
reference = initialCall.reference
} else {
reference = .id(id: callInfo.id, accessHash: callInfo.accessHash)
}
self.currentLocalSsrc = ssrc
self.requestDisposable.set((self.accountContext.engine.calls.joinGroupCall(
peerId: self.peerId,
joinAs: self.joinAsPeerId,
callId: callInfo.id,
accessHash: callInfo.accessHash,
reference: reference,
preferMuted: true,
joinPayload: joinPayload,
peerAdminIds: peerAdminIds,
inviteHash: self.invite,
keyFingerprint: self.encryptionKey?.fingerprint
generateE2E: generateE2EData
)
|> deliverOnMainQueue).start(next: { [weak self] joinCallResult in
guard let self else {
@@ -2153,6 +2283,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
self.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: self.audioSessionControl)
self.e2ePoll(subChainId: 0)
self.e2ePoll(subChainId: 1)
}, error: { [weak self] error in
guard let self else {
return
@@ -2394,11 +2527,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
serviceState = participantsContext.serviceState
}
let reference: InternalGroupCallReference
if let initialCall = self.initialCall {
reference = initialCall.reference
} else {
reference = .id(id: callInfo.id, accessHash: callInfo.accessHash)
}
let participantsContext = self.accountContext.engine.calls.groupCall(
peerId: self.peerId,
myPeerId: self.joinAsPeerId,
id: callInfo.id,
accessHash: callInfo.accessHash,
reference: reference,
state: initialState,
previousServiceState: serviceState
)
@@ -2701,8 +2841,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
isVideoEnabled: state.isVideoEnabled,
unmutedVideoLimit: state.unmutedVideoLimit,
isStream: callInfo.isStream,
upgradedPrivateCallId: callInfo.upgradedPrivateCallId
isStream: callInfo.isStream
))))
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
@@ -2759,6 +2898,117 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
private func addE2EBlocks(blocks: [Data], subChainId: Int) {
guard let initialCall = self.initialCall, let keyPair = self.keyPair else {
return
}
let (outBlocks, outEmoji) = self.e2eCall.with({ callState -> ([Data], Data) in
if let call = callState.call {
for block in blocks {
if subChainId == 0 {
call.applyBlock(block)
} else if subChainId == 1 {
call.applyBroadcastBlock(block)
}
}
return (call.takeOutgoingBroadcastBlocks(), call.emojiState())
} else {
if subChainId == 0 {
guard let block = blocks.last else {
return ([], Data())
}
guard let keyPair = TdKeyPair(keyId: keyPair.id, publicKey: keyPair.publicKey.data) else {
return ([], Data())
}
guard let call = TdCall.make(with: keyPair, latestBlock: block) else {
return ([], Data())
}
callState.call = call
for block in callState.pendingIncomingBroadcastBlocks {
call.applyBroadcastBlock(block)
}
callState.pendingIncomingBroadcastBlocks.removeAll()
return (call.takeOutgoingBroadcastBlocks(), call.emojiState())
} else if subChainId == 1 {
callState.pendingIncomingBroadcastBlocks.append(contentsOf: blocks)
return ([], Data())
} else {
return ([], Data())
}
}
})
self.e2eEncryptionKeyHashValue.set(outEmoji.isEmpty ? nil : outEmoji)
//TODO:release queue
for outBlock in outBlocks {
let _ = self.accountContext.engine.calls.sendConferenceCallBroadcast(callId: initialCall.description.id, accessHash: initialCall.description.accessHash, block: outBlock).startStandalone()
}
}
private func e2ePoll(subChainId: Int) {
guard let initialCall = self.initialCall else {
return
}
let offset: Int?
if subChainId == 0 {
offset = self.e2ePoll0Offset
self.e2ePoll0Disposable?.dispose()
} else if subChainId == 1 {
offset = self.e2ePoll1Offset
self.e2ePoll1Disposable?.dispose()
} else {
return
}
let disposable = (self.accountContext.engine.calls.pollConferenceCallBlockchain(reference: initialCall.reference, subChainId: subChainId, offset: offset ?? 0, limit: 10)
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
guard let self else {
return
}
var delayPoll = true
if let result {
if subChainId == 0 {
if self.e2ePoll0Offset != result.nextOffset {
self.e2ePoll0Offset = result.nextOffset
delayPoll = false
}
} else if subChainId == 1 {
if self.e2ePoll1Offset != result.nextOffset {
self.e2ePoll1Offset = result.nextOffset
delayPoll = false
}
}
self.addE2EBlocks(blocks: result.blocks, subChainId: subChainId)
}
if subChainId == 0 {
self.e2ePoll0Timer?.invalidate()
self.e2ePoll0Timer = Foundation.Timer.scheduledTimer(withTimeInterval: delayPoll ? 1.0 : 0.0, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
self.e2ePoll(subChainId: 0)
})
} else if subChainId == 1 {
self.e2ePoll1Timer?.invalidate()
self.e2ePoll1Timer = Foundation.Timer.scheduledTimer(withTimeInterval: delayPoll ? 1.0 : 0.0, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
self.e2ePoll(subChainId: 1)
})
}
})
if subChainId == 0 {
self.e2ePoll0Disposable = disposable
} else if subChainId == 1 {
self.e2ePoll1Disposable = disposable
}
}
private func activateIncomingAudioIfNeeded() {
if let genericCallContext = self.genericCallContext, case let .call(groupCall) = genericCallContext {
groupCall.activateIncomingAudio()
@@ -2807,7 +3057,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
if !remainingSsrcs.isEmpty, let callInfo = self.internalState.callInfo {
return (self.accountContext.engine.calls.getGroupCallParticipants(callId: callInfo.id, accessHash: callInfo.accessHash, offset: "", ssrcs: Array(remainingSsrcs), limit: 100, sortAscending: callInfo.sortAscending)
return (self.accountContext.engine.calls.getGroupCallParticipants(reference: .id(id: callInfo.id, accessHash: callInfo.accessHash), offset: "", ssrcs: Array(remainingSsrcs), limit: 100, sortAscending: callInfo.sortAscending)
|> deliverOnMainQueue).start(next: { state in
extractMediaChannelDescriptions(remainingSsrcs: &remainingSsrcs, participants: state.participants, into: &result)
@@ -2913,7 +3163,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
self.markedAsCanBeRemoved = true
self.genericCallContext?.stop(account: self.account, reportCallId: self.conferenceFromCallId, debugLog: self.debugLog)
self.genericCallContext?.stop(account: self.account, reportCallId: nil, debugLog: self.debugLog)
self.screencastIPCContext?.disableScreencast(account: self.account)
self._canBeRemoved.set(.single(true))
@@ -3615,7 +3865,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let context = self.accountContext
let currentCall: Signal<GroupCallInfo?, CallError>
if let initialCall = self.initialCall {
currentCall = context.engine.calls.getCurrentGroupCall(callId: initialCall.id, accessHash: initialCall.accessHash)
currentCall = context.engine.calls.getCurrentGroupCall(reference: initialCall.reference)
|> mapError { _ -> CallError in
return .generic
}
@@ -3623,7 +3873,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return summary?.info
}
} else if case let .active(callInfo) = self.internalState {
currentCall = context.engine.calls.getCurrentGroupCall(callId: callInfo.id, accessHash: callInfo.accessHash)
currentCall = context.engine.calls.getCurrentGroupCall(reference: .id(id: callInfo.id, accessHash: callInfo.accessHash))
|> mapError { _ -> CallError in
return .generic
}
@@ -3662,7 +3912,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
if let value = value {
self.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream)
var reference: InternalGroupCallReference = .id(id: value.id, accessHash: value.accessHash)
if let current = self.initialCall {
switch current.reference {
case .message, .link:
reference = current.reference
default:
break
}
}
self.initialCall = (EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream), reference)
self.callId = value.id
self.updateSessionState(internalState: .active(value), audioSessionControl: self.audioSessionControl)
} else {
@@ -3673,7 +3933,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
public func invitePeer(_ peerId: PeerId) -> Bool {
if self.isConference {
guard let initialCall = self.initialCall, let encryptionKey = self.encryptionKey else {
guard let initialCall = self.initialCall else {
return false
}
//TODO:release
let _ = self.accountContext.engine.calls.inviteConferenceCallParticipant(callId: initialCall.description.id, accessHash: initialCall.description.accessHash, peerId: peerId).start()
return false
/*guard let initialCall = self.initialCall else {
return false
}
if conferenceInvitationContexts[peerId] != nil {
@@ -3685,7 +3952,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let invitationContext = PendingConferenceInvitationContext(
callSessionManager: self.accountContext.account.callSessionManager,
groupCall: GroupCallReference(id: initialCall.id, accessHash: initialCall.accessHash),
encryptionKey: encryptionKey.key,
peerId: peerId,
onStateUpdated: { state in
onStateUpdated?(state)
@@ -3733,7 +3999,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
return false
return false*/
} else {
guard let callInfo = self.internalState.callInfo, !self.invitedPeersValue.contains(where: { $0.id == peerId }) else {
return false
@@ -3750,9 +4016,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
func setConferenceInvitedPeers(_ peerIds: [PeerId]) {
self.invitedPeersValue = peerIds.map {
//TODO:release
/*self.invitedPeersValue = peerIds.map {
PresentationGroupCallInvitedPeer(id: $0, state: .requesting)
}
}*/
}
public func removedPeer(_ peerId: PeerId) {
@@ -3771,6 +4038,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
public var inviteLinks: Signal<GroupCallInviteLinks?, NoError> {
let engine = self.accountContext.engine
let initialCall = self.initialCall
let isConference = self.isConference
return self.state
|> map { state -> PeerId in
@@ -3787,8 +4056,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
|> mapToSignal { state in
if let callInfo = state.callInfo {
return engine.calls.groupCallInviteLinks(callId: callInfo.id, accessHash: callInfo.accessHash)
if let callInfo = state.callInfo {
let reference: InternalGroupCallReference
if let initialCall = initialCall {
reference = initialCall.reference
} else {
reference = .id(id: callInfo.id, accessHash: callInfo.accessHash)
}
return engine.calls.groupCallInviteLinks(reference: reference, isConference: isConference)
} else {
return .complete()
}
@@ -3975,3 +4251,24 @@ private final class InProcessScreencastContext: ScreencastContext {
self.context.setJoinResponse(payload: clientParams)
}
}
public final class TelegramE2EEncryptionProviderImpl: TelegramE2EEncryptionProvider {
public static let shared = TelegramE2EEncryptionProviderImpl()
public func generateKeyPair() -> TelegramKeyPair? {
guard let keyPair = TdKeyPair.generate() else {
return nil
}
guard let publicKey = TelegramPublicKey(data: keyPair.publicKey) else {
return nil
}
return TelegramKeyPair(id: keyPair.keyId, publicKey: publicKey)
}
public func generateCallZeroBlock(keyPair: TelegramKeyPair, userId: Int64) -> Data? {
guard let keyPair = TdKeyPair(keyId: keyPair.id, publicKey: keyPair.publicKey.data) else {
return nil
}
return tdGenerateZeroBlock(keyPair, userId)
}
}