diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index 53d4d61c7f..45a7d000ac 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -498,9 +498,42 @@ public enum VideoChatCall: Equatable { } } +public extension VideoChatCall { + var accountContext: AccountContext { + switch self { + case let .group(group): + return group.accountContext + case let .conferenceSource(conferenceSource): + return conferenceSource.context + } + } +} + +public enum PresentationCurrentCall: Equatable { + case call(PresentationCall) + case group(VideoChatCall) + + public static func ==(lhs: PresentationCurrentCall, rhs: PresentationCurrentCall) -> Bool { + switch lhs { + case let .call(lhsCall): + if case let .call(rhsCall) = rhs, lhsCall === rhsCall { + return true + } else { + return false + } + case let .group(lhsCall): + if case let .group(rhsCall) = rhs, lhsCall == rhsCall { + return true + } else { + return false + } + } + } +} + public protocol PresentationCallManager: AnyObject { var currentCallSignal: Signal { get } - var currentGroupCallSignal: Signal { get } + var currentGroupCallSignal: Signal { get } var hasActiveCall: Bool { get } var hasActiveGroupCall: Bool { get } diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 6179e2bf82..fcf5d2537b 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -466,7 +466,10 @@ final class CallListControllerNode: ASDisplayNode { if let callManager = context.sharedContext.callManager { currentGroupCallPeerId = callManager.currentGroupCallSignal |> map { call -> EnginePeer.Id? in - call?.peerId + guard case let .group(call) = call else { + return nil + } + return call.peerId } |> distinctUntilChanged } else { diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index b8056bc807..535d333d26 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -77,12 +77,34 @@ private final class GlobalOverlayContainerParent: ASDisplayNode { } private final class NavigationControllerNode: ASDisplayNode { + private final class View: UIView { + private var scheduledWithLayout: (() -> Void)? + + func schedule(layout f: @escaping () -> Void) { + self.scheduledWithLayout = f + self.setNeedsLayout() + } + + override func layoutSubviews() { + super.layoutSubviews() + + if let scheduledWithLayout = self.scheduledWithLayout { + self.scheduledWithLayout = nil + scheduledWithLayout() + } + } + } + private weak var controller: NavigationController? init(controller: NavigationController) { self.controller = controller super.init() + + self.setViewBlock({ + return View(frame: CGRect()) + }) } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { @@ -100,6 +122,10 @@ private final class NavigationControllerNode: ASDisplayNode { } return false } + + func schedule(layout f: @escaping () -> Void) { + (self.view as? View)?.schedule(layout: f) + } } public protocol NavigationControllerDropContentItem: AnyObject { @@ -376,6 +402,9 @@ open class NavigationController: UINavigationController, ContainableController, self.loadView() } self.validLayout = layout + + self.scheduledLayoutTransitionRequest = nil + self.updateContainers(layout: layout, transition: transition) } @@ -1805,18 +1834,11 @@ open class NavigationController: UINavigationController, ContainableController, return nil } - private func scheduleAfterLayout(_ f: @escaping () -> Void) { - (self.view as? UITracingLayerView)?.schedule(layout: { - f() - }) - self.view.setNeedsLayout() - } - private func scheduleLayoutTransitionRequest(_ transition: ContainedViewLayoutTransition) { let requestId = self.scheduledLayoutTransitionRequestId self.scheduledLayoutTransitionRequestId += 1 self.scheduledLayoutTransitionRequest = (requestId, transition) - (self.view as? UITracingLayerView)?.schedule(layout: { [weak self] in + (self.displayNode as? NavigationControllerNode)?.schedule(layout: { [weak self] in if let strongSelf = self { if let (currentRequestId, currentRequestTransition) = strongSelf.scheduledLayoutTransitionRequest, currentRequestId == requestId { strongSelf.scheduledLayoutTransitionRequest = nil @@ -1869,7 +1891,8 @@ open class NavigationController: UINavigationController, ContainableController, } if let layout = self.validLayout { inCallStatusBar.updateState(statusBar: nil, withSafeInsets: !layout.safeInsets.top.isZero, inCallNode: forceInCallStatusBar, animated: false) - self.containerLayoutUpdated(layout, transition: transition) + self.scheduleLayoutTransitionRequest(transition) + //self.containerLayoutUpdated(layout, transition: transition) } else { self.updateInCallStatusBarState = forceInCallStatusBar } @@ -1879,7 +1902,9 @@ open class NavigationController: UINavigationController, ContainableController, inCallStatusBar?.removeFromSupernode() }) if let layout = self.validLayout { - self.containerLayoutUpdated(layout, transition: transition) + let _ = layout + self.scheduleLayoutTransitionRequest(transition) + //self.containerLayoutUpdated(layout, transition: transition) } } } diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 3d1b8f1052..7b78d3afb6 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -260,10 +260,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { case let .peer(peerId): let currentGroupCall: Signal = callManager.currentGroupCallSignal |> distinctUntilChanged(isEqual: { lhs, rhs in - return lhs?.internalId == rhs?.internalId + return lhs == rhs }) |> map { call -> PresentationGroupCall? in - guard let call = call, call.peerId == peerId && call.account.peerId == context.account.peerId else { + guard case let .group(call) = call else { + return nil + } + guard call.peerId == peerId && call.account.peerId == context.account.peerId else { return nil } return call diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index bbed98f38b..d8dcb4679c 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -62,6 +62,15 @@ final class SharedCallAudioContext { audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue)) audioSessionControl.setup(synchronous: true) } + + let audioSessionActive: Signal + if let callKitIntegration = self.callKitIntegration { + audioSessionActive = callKitIntegration.audioSessionActive + } else { + audioSessionControl.activate({ _ in }) + audioSessionActive = .single(true) + } + self.isAudioSessionActivePromise.set(audioSessionActive) } } }, deactivate: { [weak self] _ in @@ -130,7 +139,7 @@ final class SharedCallAudioContext { guard let self else { return } - let _ = self + self.audioDevice?.setIsAudioSessionActive(value) }) } diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift index f6f1714e20..71be61b833 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift @@ -61,9 +61,11 @@ public final class PresentationCallManagerImpl: PresentationCallManager { private var currentCallDisposable = MetaDisposable() private let removeCurrentCallDisposable = MetaDisposable() private let removeCurrentGroupCallDisposable = MetaDisposable() + private var callToConferenceDisposable: Disposable? + private var currentUpgradedToConferenceCallId: CallSessionInternalId? - private var currentGroupCallValue: PresentationGroupCallImpl? - private var currentGroupCall: PresentationGroupCallImpl? { + private var currentGroupCallValue: VideoChatCall? + private var currentGroupCall: VideoChatCall? { return self.currentGroupCallValue } @@ -95,8 +97,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager { return self.currentCallPromise.get() } - private let currentGroupCallPromise = Promise(nil) - public var currentGroupCallSignal: Signal { + private let currentGroupCallPromise = Promise(nil) + public var currentGroupCallSignal: Signal { return self.currentGroupCallPromise.get() } @@ -297,16 +299,23 @@ public final class PresentationCallManagerImpl: PresentationCallManager { self.startCallDisposable.dispose() self.proxyServerDisposable?.dispose() self.callSettingsDisposable?.dispose() + self.callToConferenceDisposable?.dispose() } private func ringingStatesUpdated(_ ringingStates: [(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) { if let firstState = ringingStates.first { if self.currentCall == nil && self.currentGroupCall == nil { - self.currentCallDisposable.set((combineLatest(firstState.0.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1)) + self.currentCallDisposable.set((combineLatest( + firstState.0.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, PreferencesKeys.appConfiguration]) |> take(1), + accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1) + ) |> deliverOnMainQueue).start(next: { [weak self] preferences, sharedData in guard let strongSelf = self else { return } + if strongSelf.currentUpgradedToConferenceCallId == firstState.2.id { + return + } let configuration = preferences.values[PreferencesKeys.voipConfiguration]?.get(VoipConfiguration.self) ?? .defaultValue let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings]?.get(AutodownloadSettings.self) ?? .defaultSettings @@ -338,18 +347,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager { preferredVideoCodec: experimentalSettings.preferredVideoCodec ) strongSelf.updateCurrentCall(call) - strongSelf.currentCallPromise.set(.single(call)) - strongSelf.hasActivePersonalCallsPromise.set(true) - strongSelf.removeCurrentCallDisposable.set((call.canBeRemoved - |> deliverOnMainQueue).start(next: { [weak self, weak call] value in - if value, let strongSelf = self, let call = call { - if strongSelf.currentCall === call { - strongSelf.updateCurrentCall(nil) - strongSelf.currentCallPromise.set(.single(nil)) - strongSelf.hasActivePersonalCallsPromise.set(false) - } - } - })) })) } else { for (context, _, state, _, _) in ringingStates { @@ -370,12 +367,15 @@ public final class PresentationCallManagerImpl: PresentationCallManager { alreadyInCallWithPeerId = call.peerId } else if let currentGroupCall = self.currentGroupCallValue { alreadyInCall = true - alreadyInCallWithPeerId = currentGroupCall.peerId + switch currentGroupCall { + case let .conferenceSource(conferenceSource): + alreadyInCallWithPeerId = conferenceSource.peerId + case let .group(groupCall): + alreadyInCallWithPeerId = groupCall.peerId + } } else { - if #available(iOS 10.0, *) { - if CXCallObserver().calls.contains(where: { $0.hasEnded == false }) { - alreadyInCall = true - } + if CXCallObserver().calls.contains(where: { $0.hasEnded == false }) { + alreadyInCall = true } } @@ -456,12 +456,22 @@ public final class PresentationCallManagerImpl: PresentationCallManager { begin() })) } else if let currentGroupCall = self.currentGroupCallValue { - self.startCallDisposable.set((currentGroupCall.leave(terminateIfPossible: false) - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue).start(next: { _ in - begin() - })) + switch currentGroupCall { + case let .conferenceSource(conferenceSource): + self.startCallDisposable.set((conferenceSource.hangUp() + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + case let .group(groupCall): + self.startCallDisposable.set((groupCall.leave(terminateIfPossible: false) + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + } } else { begin() } @@ -478,12 +488,22 @@ public final class PresentationCallManagerImpl: PresentationCallManager { begin() })) } else if let currentGroupCall = self.currentGroupCallValue { - self.startCallDisposable.set((currentGroupCall.leave(terminateIfPossible: false) - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue).start(next: { _ in - begin() - })) + switch currentGroupCall { + case let .conferenceSource(conferenceSource): + self.startCallDisposable.set((conferenceSource.hangUp() + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + case let .group(groupCall): + self.startCallDisposable.set((groupCall.leave(terminateIfPossible: false) + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + } } else { begin() } @@ -540,9 +560,18 @@ public final class PresentationCallManagerImpl: PresentationCallManager { return context.account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, conferenceCall: nil, internalId: internalId) } - return (combineLatest(queue: .mainQueue(), request, networkType |> take(1), context.account.postbox.peerView(id: peerId) |> map { peerView -> Bool in - return peerView.peerIsContact - } |> take(1), context.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), areVideoCallsAvailable) + return (combineLatest(queue: .mainQueue(), + request, + networkType |> take(1), + context.account.postbox.peerView(id: peerId) + |> map { peerView -> Bool in + return peerView.peerIsContact + } + |> take(1), + context.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, PreferencesKeys.appConfiguration]) |> take(1), + accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), + areVideoCallsAvailable + ) |> deliverOnMainQueue |> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, areVideoCallsAvailable in if let strongSelf = self, accessEnabled { @@ -586,18 +615,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager { preferredVideoCodec: experimentalSettings.preferredVideoCodec ) strongSelf.updateCurrentCall(call) - strongSelf.currentCallPromise.set(.single(call)) - strongSelf.hasActivePersonalCallsPromise.set(true) - strongSelf.removeCurrentCallDisposable.set((call.canBeRemoved - |> deliverOnMainQueue).start(next: { [weak call] value in - if value, let strongSelf = self, let call = call { - if strongSelf.currentCall === call { - strongSelf.updateCurrentCall(nil) - strongSelf.currentCallPromise.set(.single(nil)) - strongSelf.hasActivePersonalCallsPromise.set(false) - } - } - })) } }) |> mapToSignal { value -> Signal in @@ -613,7 +630,48 @@ public final class PresentationCallManagerImpl: PresentationCallManager { self.resumeMedia = self.isMediaPlaying() } - self.currentCallValue = value + if self.currentCallValue !== value { + self.currentCallValue = value + + self.callToConferenceDisposable?.dispose() + self.callToConferenceDisposable = nil + self.currentUpgradedToConferenceCallId = nil + + if let currentCallValue = self.currentCallValue { + self.callToConferenceDisposable = (currentCallValue.conferenceState + |> filter { conferenceState in + return conferenceState != nil + } + |> take(1) + |> deliverOnMainQueue).startStrict(next: { [weak self, weak currentCallValue] _ in + guard let self, let currentCallValue, self.currentCallValue === currentCallValue else { + return + } + + self.currentUpgradedToConferenceCallId = currentCallValue.internalId + self.removeCurrentCallDisposable.set(nil) + + self.updateCurrentGroupCall(.conferenceSource(currentCallValue)) + self.updateCurrentCall(nil) + }) + + self.currentCallPromise.set(.single(currentCallValue)) + self.hasActivePersonalCallsPromise.set(true) + self.removeCurrentCallDisposable.set((currentCallValue.canBeRemoved + |> deliverOnMainQueue).start(next: { [weak self, weak currentCallValue] value in + if value, let self, let currentCallValue { + if self.currentCall === currentCallValue { + self.updateCurrentCall(nil) + self.currentCallPromise.set(.single(nil)) + self.hasActivePersonalCallsPromise.set(false) + } + } + })) + } else { + self.currentCallPromise.set(.single(nil)) + self.hasActivePersonalCallsPromise.set(false) + } + } if !wasEmpty && isEmpty && self.resumeMedia { self.resumeMedia = false @@ -621,14 +679,76 @@ public final class PresentationCallManagerImpl: PresentationCallManager { } } - private func updateCurrentGroupCall(_ value: PresentationGroupCallImpl?) { + private func updateCurrentGroupCall(_ value: VideoChatCall?) { let wasEmpty = self.currentGroupCallValue == nil let isEmpty = value == nil if wasEmpty && !isEmpty { self.resumeMedia = self.isMediaPlaying() } - self.currentGroupCallValue = value + if self.currentGroupCallValue != value { + self.currentGroupCallValue = value + + if let value { + switch value { + case let .conferenceSource(conferenceSource): + self.callToConferenceDisposable?.dispose() + self.callToConferenceDisposable = (conferenceSource.conferenceState + |> filter { value in + if let value, case .ready = value { + return true + } else { + return false + } + } + |> take(1) + |> deliverOnMainQueue).startStrict(next: { [weak self, weak conferenceSource] _ in + guard let self, let conferenceSource, self.currentGroupCallValue == .conferenceSource(conferenceSource) else { + return + } + guard let groupCall = conferenceSource.conferenceCall else { + return + } + self.updateCurrentGroupCall(.group(groupCall)) + }) + + self.currentGroupCallPromise.set(.single(.conferenceSource(conferenceSource))) + self.hasActiveGroupCallsPromise.set(true) + self.removeCurrentGroupCallDisposable.set((conferenceSource.canBeRemoved + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self, weak conferenceSource] value in + guard let self, let conferenceSource else { + return + } + if value { + if self.currentGroupCall == .conferenceSource(conferenceSource) { + self.updateCurrentGroupCall(nil) + } + } + })) + case let .group(groupCall): + self.currentGroupCallPromise.set(.single(.group(groupCall))) + self.hasActiveGroupCallsPromise.set(true) + self.removeCurrentGroupCallDisposable.set((groupCall.canBeRemoved + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self, weak groupCall] value in + guard let self, let groupCall else { + return + } + if value { + if self.currentGroupCall == .group(groupCall) { + self.updateCurrentGroupCall(nil) + } + } + })) + } + } else { + self.currentGroupCallPromise.set(.single(nil)) + self.hasActiveGroupCallsPromise.set(false) + } + } if !wasEmpty && isEmpty && self.resumeMedia { self.resumeMedia = false @@ -671,7 +791,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { ) |> deliverOnMainQueue |> mapToSignal { [weak self, weak parentController] accessEnabled, peer -> Signal in - guard let strongSelf = self else { + guard let self else { return .single(false) } @@ -684,93 +804,36 @@ public final class PresentationCallManagerImpl: PresentationCallManager { isChannel = true } - if shouldUseV2VideoChatImpl(context: accountContext) { - if let parentController { - parentController.push(ScheduleVideoChatSheetScreen( - context: accountContext, - scheduleAction: { timestamp in - guard let self else { - return - } - - let call = PresentationGroupCallImpl( - accountContext: accountContext, - audioSession: self.audioSession, - callKitIntegration: nil, - getDeviceAccessData: self.getDeviceAccessData, - initialCall: nil, - internalId: internalId, - peerId: peerId, - isChannel: isChannel, - invite: nil, - joinAsPeerId: nil, - isStream: false, - encryptionKey: nil, - conferenceFromCallId: nil, - isConference: false, - sharedAudioContext: nil - ) - call.schedule(timestamp: timestamp) - - self.updateCurrentGroupCall(call) - self.currentGroupCallPromise.set(.single(call)) - self.hasActiveGroupCallsPromise.set(true) - self.removeCurrentGroupCallDisposable.set((call.canBeRemoved - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self, weak call] value in - guard let self, let call else { - return - } - if value { - if self.currentGroupCall === call { - self.updateCurrentGroupCall(nil) - self.currentGroupCallPromise.set(.single(nil)) - self.hasActiveGroupCallsPromise.set(false) - } - } - })) + if let parentController { + parentController.push(ScheduleVideoChatSheetScreen( + context: accountContext, + scheduleAction: { [weak self] timestamp in + guard let self else { + return } - )) - } - - return .single(true) - } else { - let call = PresentationGroupCallImpl( - accountContext: accountContext, - audioSession: strongSelf.audioSession, - callKitIntegration: nil, - getDeviceAccessData: strongSelf.getDeviceAccessData, - initialCall: nil, - internalId: internalId, - peerId: peerId, - isChannel: isChannel, - invite: nil, - joinAsPeerId: nil, - isStream: false, - encryptionKey: nil, - conferenceFromCallId: nil, - isConference: false, - sharedAudioContext: nil - ) - strongSelf.updateCurrentGroupCall(call) - strongSelf.currentGroupCallPromise.set(.single(call)) - strongSelf.hasActiveGroupCallsPromise.set(true) - strongSelf.removeCurrentGroupCallDisposable.set((call.canBeRemoved - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue).start(next: { [weak call] value in - guard let strongSelf = self, let call = call else { - return + + let call = PresentationGroupCallImpl( + accountContext: accountContext, + audioSession: self.audioSession, + callKitIntegration: nil, + getDeviceAccessData: self.getDeviceAccessData, + initialCall: nil, + internalId: internalId, + peerId: peerId, + isChannel: isChannel, + invite: nil, + joinAsPeerId: nil, + isStream: false, + encryptionKey: nil, + conferenceFromCallId: nil, + isConference: false, + sharedAudioContext: nil + ) + call.schedule(timestamp: timestamp) + + self.updateCurrentGroupCall(.group(call)) } - if value { - if strongSelf.currentGroupCall === call { - strongSelf.updateCurrentGroupCall(nil) - strongSelf.currentGroupCallPromise.set(.single(nil)) - strongSelf.hasActiveGroupCallsPromise.set(false) - } - } - })) + )) } return .single(true) @@ -787,15 +850,29 @@ public final class PresentationCallManagerImpl: PresentationCallManager { if let currentGroupCall = self.currentGroupCallValue { if endCurrentIfAny { - let endSignal = currentGroupCall.leave(terminateIfPossible: false) - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue - self.startCallDisposable.set(endSignal.start(next: { _ in - begin() - })) + switch currentGroupCall { + case let .conferenceSource(conferenceSource): + self.startCallDisposable.set((conferenceSource.hangUp() + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + case let .group(groupCall): + self.startCallDisposable.set((groupCall.leave(terminateIfPossible: false) + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + } } else { - return .alreadyInProgress(currentGroupCall.peerId) + switch currentGroupCall { + case let .conferenceSource(conferenceSource): + return .alreadyInProgress(conferenceSource.peerId) + case let .group(groupCall): + return .alreadyInProgress(groupCall.peerId) + } } } else if let currentCall = self.currentCall { if endCurrentIfAny { @@ -832,15 +909,29 @@ public final class PresentationCallManagerImpl: PresentationCallManager { if let currentGroupCall = self.currentGroupCallValue { if endCurrentIfAny { - let endSignal = currentGroupCall.leave(terminateIfPossible: false) - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue - self.startCallDisposable.set(endSignal.start(next: { _ in - begin() - })) + switch currentGroupCall { + case let .conferenceSource(conferenceSource): + self.startCallDisposable.set((conferenceSource.hangUp() + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + case let .group(groupCall): + self.startCallDisposable.set((groupCall.leave(terminateIfPossible: false) + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { _ in + begin() + })) + } } else { - return .alreadyInProgress(currentGroupCall.peerId) + switch currentGroupCall { + case let .conferenceSource(conferenceSource): + return .alreadyInProgress(conferenceSource.peerId) + case let .group(groupCall): + return .alreadyInProgress(groupCall.peerId) + } } } else if let currentCall = self.currentCall { if endCurrentIfAny { @@ -919,48 +1010,51 @@ public final class PresentationCallManagerImpl: PresentationCallManager { return .single(false) } - var isChannel = false - if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info { - isChannel = true - } - - let call = PresentationGroupCallImpl( + strongSelf.createGroupCall( accountContext: accountContext, - audioSession: strongSelf.audioSession, - callKitIntegration: nil, - getDeviceAccessData: strongSelf.getDeviceAccessData, + peerId: peerId, + peer: peer, initialCall: initialCall, internalId: internalId, - peerId: peerId, - isChannel: isChannel, invite: invite, - joinAsPeerId: joinAsPeerId, - isStream: initialCall.isStream ?? false, - encryptionKey: nil, - conferenceFromCallId: nil, - isConference: false, - sharedAudioContext: nil + joinAsPeerId: joinAsPeerId ) - strongSelf.updateCurrentGroupCall(call) - strongSelf.currentGroupCallPromise.set(.single(call)) - strongSelf.hasActiveGroupCallsPromise.set(true) - strongSelf.removeCurrentGroupCallDisposable.set((call.canBeRemoved - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue).start(next: { [weak call] value in - guard let strongSelf = self, let call = call else { - return - } - if value { - if strongSelf.currentGroupCall === call { - strongSelf.updateCurrentGroupCall(nil) - strongSelf.currentGroupCallPromise.set(.single(nil)) - strongSelf.hasActiveGroupCallsPromise.set(false) - } - } - })) return .single(true) } } + + private func createGroupCall( + accountContext: AccountContext, + peerId: EnginePeer.Id, + peer: EnginePeer?, + initialCall: EngineGroupCallDescription, + internalId: CallSessionInternalId, + invite: String?, + joinAsPeerId: EnginePeer.Id? + ) { + var isChannel = false + if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info { + isChannel = true + } + + let call = PresentationGroupCallImpl( + accountContext: accountContext, + audioSession: self.audioSession, + callKitIntegration: nil, + getDeviceAccessData: self.getDeviceAccessData, + initialCall: initialCall, + internalId: internalId, + peerId: peerId, + isChannel: isChannel, + invite: invite, + joinAsPeerId: joinAsPeerId, + isStream: initialCall.isStream ?? false, + encryptionKey: nil, + conferenceFromCallId: nil, + isConference: false, + sharedAudioContext: nil + ) + self.updateCurrentGroupCall(.group(call)) + } } diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index cdacb13681..436835c834 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -1111,6 +1111,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.conferenceFromCallId = conferenceFromCallId self.isConference = isConference self.encryptionKey = encryptionKey + + var sharedAudioContext = sharedAudioContext + if sharedAudioContext == nil && accountContext.sharedContext.immediateExperimentalUISettings.conferenceCalls { + let sharedAudioContextValue = SharedCallAudioContext(audioSession: audioSession, callKitIntegration: callKitIntegration) + sharedAudioContext = sharedAudioContextValue + } + self.sharedAudioContext = sharedAudioContext if self.sharedAudioContext == nil && !accountContext.sharedContext.immediateExperimentalUISettings.liveStreamV2 { @@ -1906,7 +1913,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { var encryptionKey: Data? encryptionKey = self.encryptionKey?.key - encryptionKey = nil let contextAudioSessionActive: Signal if self.sharedAudioContext != nil { @@ -2875,18 +2881,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { let _ = (callManager.currentGroupCallSignal |> take(1) |> deliverOnMainQueue).start(next: { [weak self] call in - guard let strongSelf = self else { + guard let self else { return } - if let call = call, call !== strongSelf { - strongSelf.wasRemoved.set(.single(true)) + if let call = call, call != .group(self) { + self.wasRemoved.set(.single(true)) return } - strongSelf.beginTone(tone: .groupLeft) + self.beginTone(tone: .groupLeft) Queue.mainQueue().after(1.0, { - strongSelf.wasRemoved.set(.single(true)) + self.wasRemoved.set(.single(true)) }) }) } diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift index faa3b83808..3077cd803e 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift @@ -24,16 +24,7 @@ import TelegramAudio import LegacyComponents import TooltipUI -extension VideoChatCall { - var accountContext: AccountContext { - switch self { - case let .group(group): - return group.accountContext - case let .conferenceSource(conferenceSource): - return conferenceSource.context - } - } - +extension VideoChatCall { var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> { switch self { case let .group(group): diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift b/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift index 25280946e0..586d5d3c08 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift @@ -98,7 +98,7 @@ public final class VoiceChatJoinScreen: ViewController { currentGroupCall = callManager.currentGroupCallSignal |> castError(GetCurrentGroupCallError.self) |> mapToSignal { call -> Signal<(PresentationGroupCall, Int64, Bool)?, GetCurrentGroupCallError> in - if let call = call { + if case let .group(call) = call { return call.summaryState |> castError(GetCurrentGroupCallError.self) |> map { state -> (PresentationGroupCall, Int64, Bool)? in diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/KeyEmojiView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/KeyEmojiView.swift index 21814cde23..9e4b4ede4f 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/KeyEmojiView.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/KeyEmojiView.swift @@ -93,7 +93,7 @@ final class KeyEmojiView: HighlightTrackingButton { for i in 0 ..< self.emojiViews.count { let emojiView = self.emojiViews[i] emojiView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - emojiView.layer.animatePosition(from: CGPoint(x: CGFloat(i) * 30.0, y: 0.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + //emojiView.layer.animateScale(from: 0.2, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) } } @@ -109,7 +109,7 @@ final class KeyEmojiView: HighlightTrackingButton { } private func update(params: Params, transition: ComponentTransition) -> CGSize { - let itemSpacing: CGFloat = 1.0 + let itemSpacing: CGFloat = 0.0 var height: CGFloat = 0.0 var nextX = 0.0 @@ -118,7 +118,7 @@ final class KeyEmojiView: HighlightTrackingButton { nextX += itemSpacing } let emojiView = self.emojiViews[i] - let itemSize = emojiView.update(string: emoji[i], fontSize: params.isExpanded ? 40.0 : 16.0, fontWeight: 0.0, color: .white, constrainedWidth: 100.0, transition: transition) + let itemSize = emojiView.update(string: emoji[i], fontSize: params.isExpanded ? 40.0 : 15.0, fontWeight: 0.0, color: .white, constrainedWidth: 100.0, transition: transition) if height == 0.0 { height = itemSize.height } @@ -144,7 +144,7 @@ func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to targetPosit let numPoints: Int = Int(ceil(Double(UIScreen.main.maximumFramesPerSecond) * duration)) var keyframes: [CGPoint] = [] - if abs(y1 - y3) < 5.0 && abs(x1 - x3) < 5.0 { + if abs(y1 - y3) < 5.0 || abs(x1 - x3) < 5.0 { for rawI in 0 ..< numPoints { let i = reverse ? (numPoints - 1 - rawI) : rawI let ks = CGFloat(i) / CGFloat(numPoints - 1) diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift index b9ff915b87..54e6187304 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift @@ -1241,8 +1241,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu let titleSize = self.titleView.update( string: titleString, - fontSize: !havePrimaryVideo ? 28.0 : 17.0, - fontWeight: !havePrimaryVideo ? 0.0 : 0.25, + fontSize: 28.0, + fontWeight: 0.0, color: .white, constrainedWidth: params.size.width - 16.0 * 2.0, transition: transition @@ -1301,7 +1301,11 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu if currentAreControlsHidden { titleY = -8.0 - titleSize.height - statusSize.height } else if havePrimaryVideo { - titleY = params.insets.top + 2.0 + if case .active = params.state.lifecycleState { + titleY = params.insets.top + 2.0 + 54.0 + } else { + titleY = params.insets.top + 2.0 + } } else { titleY = collapsedAvatarFrame.maxY + 39.0 } @@ -1313,7 +1317,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu size: titleSize ) transition.setFrame(view: self.titleView, frame: titleFrame) - genericAlphaTransition.setAlpha(view: self.titleView, alpha: (currentAreControlsHidden || self.isAnimatedOutToGroupCall) ? 0.0 : 1.0) + genericAlphaTransition.setAlpha(view: self.titleView, alpha: (currentAreControlsHidden || self.isAnimatedOutToGroupCall || (havePrimaryVideo && self.isEmojiKeyExpanded)) ? 0.0 : 1.0) var emojiViewSizeValue: CGSize? var emojiTransition = transition @@ -1392,15 +1396,10 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu } } - var statusEmojiWidth: CGFloat = 0.0 - let statusEmojiSpacing: CGFloat = 8.0 - if !self.isEmojiKeyExpanded, let emojiViewSize = emojiViewSizeValue { - statusEmojiWidth = statusEmojiSpacing + emojiViewSize.width - } let statusFrame = CGRect( origin: CGPoint( - x: (params.size.width - statusSize.width - statusEmojiWidth) * 0.5, - y: titleFrame.maxY + (havePrimaryVideo ? 0.0 : 4.0) + x: (params.size.width - statusSize.width) * 0.5, + y: titleFrame.maxY + 4.0 ), size: statusSize ) @@ -1414,12 +1413,19 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu } } else { transition.setFrame(view: self.statusView, frame: statusFrame) - genericAlphaTransition.setAlpha(view: self.statusView, alpha: (currentAreControlsHidden || self.isAnimatedOutToGroupCall) ? 0.0 : 1.0) + genericAlphaTransition.setAlpha(view: self.statusView, alpha: (currentAreControlsHidden || self.isAnimatedOutToGroupCall || (havePrimaryVideo && self.isEmojiKeyExpanded)) ? 0.0 : 1.0) } if case .active = params.state.lifecycleState { if let emojiView = self.emojiView, !self.isEmojiKeyExpanded, let emojiViewSize = emojiViewSizeValue { - let emojiViewFrame = CGRect(origin: CGPoint(x: statusFrame.maxX + statusEmojiSpacing, y: statusFrame.minY + floorToScreenPixels((statusFrame.height - emojiViewSize.height) * 0.5)), size: emojiViewSize) + let emojiViewY: CGFloat + if currentAreControlsHidden { + emojiViewY = -8.0 - emojiViewSize.height + } else { + emojiViewY = params.insets.top + 14.0 + } + + let emojiViewFrame = CGRect(origin: CGPoint(x: floor((params.size.width - emojiViewSize.width) * 0.5), y: emojiViewY), size: emojiViewSize) if case let .curve(duration, curve) = transition.animation, emojiViewWasExpanded { let distance = CGPoint(x: emojiViewFrame.midX - emojiView.center.x, y: emojiViewFrame.midY - emojiView.center.y) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 4bc8355609..a98781aa0a 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -163,17 +163,18 @@ public final class SharedAccountContextImpl: SharedAccountContext { public var callManager: PresentationCallManager? let hasInAppPurchases: Bool - private var callDisposable: Disposable? private var callStateDisposable: Disposable? private(set) var currentCallStatusBarNode: CallStatusBarNodeImpl? + private var callDisposable: Disposable? private var groupCallDisposable: Disposable? private var callController: CallController? - private var call: PresentationCall? + + private var currentCall: PresentationCurrentCall? + public let hasOngoingCall = ValuePromise(false) - private let callState = Promise(nil) private var awaitingCallConnectionDisposable: Disposable? private var callPeerDisposable: Disposable? private var callIsConferenceDisposable: Disposable? @@ -182,7 +183,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { public var currentGroupCallController: ViewController? { return self.groupCallController } - private let hasGroupCallOnScreenPromise = ValuePromise(false, ignoreRepeated: true) + private let hasGroupCallOnScreenPromise = Promise(false) public var hasGroupCallOnScreen: Signal { return self.hasGroupCallOnScreenPromise.get() } @@ -805,8 +806,14 @@ public final class SharedAccountContextImpl: SharedAccountContext { guard let self else { return } - - if call !== self.call { + + if let call { + self.updateCurrentCall(call: .call(call)) + } else if let current = self.currentCall, case .call = current { + self.updateCurrentCall(call: nil) + } + + /*if call !== self.call { let previousCall = self.call self.call = call @@ -842,11 +849,6 @@ public final class SharedAccountContextImpl: SharedAccountContext { self.callIsConferenceDisposable = nil if let call { - if call.conferenceStateValue == nil && call.conferenceCall == nil { - self.callState.set(call.state - |> map(Optional.init)) - } - self.hasOngoingCall.set(true) setNotificationCall(call) @@ -862,14 +864,12 @@ public final class SharedAccountContextImpl: SharedAccountContext { } guard let callController = self.callController, callController.call === call else { if self.callController == nil, call.conferenceStateValue != nil { - self.callState.set(.single(nil)) self.presentControllerWithCurrentCall() self.notificationController?.setBlocking(nil) } return } if call.conferenceStateValue != nil { - self.callState.set(.single(nil)) self.presentControllerWithCurrentCall() } }) @@ -932,168 +932,99 @@ public final class SharedAccountContextImpl: SharedAccountContext { self.awaitingCallConnectionDisposable = nil setNotificationCall(nil) } - } + }*/ }) self.groupCallDisposable = (callManager.currentGroupCallSignal |> deliverOnMainQueue).start(next: { [weak self] call in - if let strongSelf = self { - if call.flatMap(VideoChatCall.group) != strongSelf.groupCallController?.call { - strongSelf.groupCallController?.dismiss(closing: true, manual: false) - strongSelf.groupCallController = nil - strongSelf.hasOngoingCall.set(false) + guard let self else { + return + } + + if let call { + self.updateCurrentCall(call: .group(call)) + } else if let current = self.currentCall, case .group = current { + self.updateCurrentCall(call: nil) + } + + /*if call.flatMap(VideoChatCall.group) != self.groupCallController?.call { + self.groupCallController?.dismiss(closing: true, manual: false) + self.groupCallController = nil + self.hasOngoingCall.set(false) + + if let call = call, let navigationController = mainWindow.viewController as? NavigationController { + mainWindow.hostView.containerView.endEditing(true) - if let call = call, let navigationController = mainWindow.viewController as? NavigationController { - mainWindow.hostView.containerView.endEditing(true) + if call.isStream { + self.hasGroupCallOnScreenPromise.set(true) + let groupCallController = MediaStreamComponentController(call: call) + groupCallController.onViewDidAppear = { [weak self] in + if let self { + self.hasGroupCallOnScreenPromise.set(true) + } + } + groupCallController.onViewDidDisappear = { [weak self] in + if let self { + self.hasGroupCallOnScreenPromise.set(false) + } + } + groupCallController.navigationPresentation = .flatModal + groupCallController.parentNavigationController = navigationController + self.groupCallController = groupCallController + navigationController.pushViewController(groupCallController) + } else { + self.hasGroupCallOnScreenPromise.set(true) - if call.isStream { - strongSelf.hasGroupCallOnScreenPromise.set(true) - let groupCallController = MediaStreamComponentController(call: call) + let _ = (makeVoiceChatControllerInitialData(sharedContext: self, accountContext: call.accountContext, call: .group(call)) + |> deliverOnMainQueue).start(next: { [weak self, weak navigationController] initialData in + guard let self, let navigationController else { + return + } + + let groupCallController = makeVoiceChatController(sharedContext: self, accountContext: call.accountContext, call: .group(call), initialData: initialData, sourceCallController: nil) groupCallController.onViewDidAppear = { [weak self] in - if let strongSelf = self { - strongSelf.hasGroupCallOnScreenPromise.set(true) + if let self { + self.hasGroupCallOnScreenPromise.set(true) } } groupCallController.onViewDidDisappear = { [weak self] in - if let strongSelf = self { - strongSelf.hasGroupCallOnScreenPromise.set(false) + if let self { + self.hasGroupCallOnScreenPromise.set(false) } } groupCallController.navigationPresentation = .flatModal groupCallController.parentNavigationController = navigationController strongSelf.groupCallController = groupCallController navigationController.pushViewController(groupCallController) - } else { - strongSelf.hasGroupCallOnScreenPromise.set(true) - - let _ = (makeVoiceChatControllerInitialData(sharedContext: strongSelf, accountContext: call.accountContext, call: .group(call)) - |> deliverOnMainQueue).start(next: { [weak strongSelf, weak navigationController] initialData in - guard let strongSelf, let navigationController else { - return - } - - let groupCallController = makeVoiceChatController(sharedContext: strongSelf, accountContext: call.accountContext, call: .group(call), initialData: initialData, sourceCallController: nil) - groupCallController.onViewDidAppear = { [weak strongSelf] in - if let strongSelf { - strongSelf.hasGroupCallOnScreenPromise.set(true) - } - } - groupCallController.onViewDidDisappear = { [weak strongSelf] in - if let strongSelf { - strongSelf.hasGroupCallOnScreenPromise.set(false) - } - } - groupCallController.navigationPresentation = .flatModal - groupCallController.parentNavigationController = navigationController - strongSelf.groupCallController = groupCallController - navigationController.pushViewController(groupCallController) - }) - } - - strongSelf.hasOngoingCall.set(true) - } else { - strongSelf.hasOngoingCall.set(false) + }) } - } - } - }) - - let callSignal: Signal<(PresentationCall?, PresentationGroupCall?), NoError> = .single((nil, nil)) - |> then( - callManager.currentCallSignal - |> deliverOnMainQueue - |> mapToSignal { call -> Signal<(PresentationCall?, PresentationGroupCall?), NoError> in - guard let call else { - return .single((nil, nil)) - } - return call.state - |> map { [weak call] state -> (PresentationCall?, PresentationGroupCall?) in - guard let call else { - return (nil, nil) - } - switch state.state { - case .ringing: - return (nil, nil) - case .terminating, .terminated: - return (nil, nil) - default: - return (call, nil) - } - } - } - |> distinctUntilChanged(isEqual: { lhs, rhs in - return lhs.0 === rhs.0 && lhs.1 === rhs.1 - }) - ) - let groupCallSignal: Signal = .single(nil) - |> then( - callManager.currentGroupCallSignal - ) - - self.callStateDisposable = combineLatest(queue: .mainQueue(), - callSignal, - groupCallSignal, - self.hasGroupCallOnScreenPromise.get() - ).start(next: { [weak self] call, groupCall, hasGroupCallOnScreen in - if let strongSelf = self { - var (call, conferenceCall) = call - var groupCall = groupCall - if let conferenceCall { - call = nil - groupCall = conferenceCall - } - - let statusBarContent: CallStatusBarNodeImpl.Content? - if let call, !hasGroupCallOnScreen { - statusBarContent = .call(strongSelf, call.context.account, call) - } else if let groupCall = groupCall, !hasGroupCallOnScreen { - statusBarContent = .groupCall(strongSelf, groupCall.account, groupCall) + + self.hasOngoingCall.set(true) } else { - statusBarContent = nil + self.hasOngoingCall.set(false) } - - var resolvedCallStatusBarNode: CallStatusBarNodeImpl? - if let statusBarContent = statusBarContent { - if let current = strongSelf.currentCallStatusBarNode { - resolvedCallStatusBarNode = current - } else { - resolvedCallStatusBarNode = CallStatusBarNodeImpl() - strongSelf.currentCallStatusBarNode = resolvedCallStatusBarNode - } - resolvedCallStatusBarNode?.update(content: statusBarContent) - } else { - strongSelf.currentCallStatusBarNode = nil - } - - if let navigationController = strongSelf.mainWindow?.viewController as? NavigationController { - navigationController.setForceInCallStatusBar(resolvedCallStatusBarNode) - } - } + }*/ }) mainWindow.inCallNavigate = { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - if let callController = strongSelf.callController { - if callController.isNodeLoaded { - mainWindow.hostView.containerView.endEditing(true) - if callController.view.superview == nil { - if useFlatModalCallsPresentation(context: callController.call.context) { - (mainWindow.viewController as? NavigationController)?.pushViewController(callController) - } else { - mainWindow.present(callController, on: .calls) - } + if let callController = self.callController { + mainWindow.hostView.containerView.endEditing(true) + if callController.view.superview == nil { + if useFlatModalCallsPresentation(context: callController.call.context) { + (mainWindow.viewController as? NavigationController)?.pushViewController(callController) } else { - callController.expandFromPipIfPossible() + mainWindow.present(callController, on: .calls) } + } else { + callController.expandFromPipIfPossible() } - } else if let groupCallController = strongSelf.groupCallController { - if groupCallController.isNodeLoaded { - mainWindow.hostView.containerView.endEditing(true) - if groupCallController.view.superview == nil { - (mainWindow.viewController as? NavigationController)?.pushViewController(groupCallController) - } + } else if let groupCallController = self.groupCallController { + mainWindow.hostView.containerView.endEditing(true) + if groupCallController.view.superview == nil { + (mainWindow.viewController as? NavigationController)?.pushViewController(groupCallController) } } } @@ -1236,8 +1167,271 @@ public final class SharedAccountContextImpl: SharedAccountContext { } } + private func updateCurrentCall(call: PresentationCurrentCall?) { + if self.currentCall == call { + return + } + + if let currentCall = self.currentCall { + if case .call = currentCall { + self.callPeerDisposable?.dispose() + self.callPeerDisposable = nil + + self.awaitingCallConnectionDisposable?.dispose() + self.awaitingCallConnectionDisposable = nil + + self.notificationController?.setBlocking(nil) + } + + self.callStateDisposable?.dispose() + self.callStateDisposable = nil + } + + self.currentCall = call + + let beginDisplayingCallStatusBar = Promise() + + var transitioningToConferenceCallController: CallController? + if let call, case let .group(groupCall) = call, case let .conferenceSource(conferenceSource) = groupCall, let callController = self.callController, callController.call === conferenceSource { + transitioningToConferenceCallController = callController + if callController.navigationPresentation != .flatModal { + callController.dismissWithoutAnimation() + } + self.callController = nil + } else { + self.hasGroupCallOnScreenPromise.set(.single(false)) + } + + if let callController = self.callController { + self.callController = nil + callController.dismiss() + } + if let groupCallController = self.groupCallController { + self.groupCallController = nil + groupCallController.dismiss() + } + + if case let .call(call) = call { + let callController = CallController(sharedContext: self, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild) + self.callController = callController + let thisCallIsOnScreenPromise = ValuePromise(false, ignoreRepeated: true) + callController.restoreUIForPictureInPicture = { [weak self, weak callController] completion in + guard let self, let callController else { + completion(false) + return + } + if callController.window == nil { + if useFlatModalCallsPresentation(context: callController.call.context) { + (self.mainWindow?.viewController as? NavigationController)?.pushViewController(callController) + } else { + self.mainWindow?.present(callController, on: .calls) + } + } + completion(true) + } + callController.onViewDidAppear = { + thisCallIsOnScreenPromise.set(true) + } + callController.onViewDidDisappear = { + thisCallIsOnScreenPromise.set(false) + } + + if call.isOutgoing { + self.mainWindow?.hostView.containerView.endEditing(true) + + thisCallIsOnScreenPromise.set(true) + self.hasGroupCallOnScreenPromise.set(thisCallIsOnScreenPromise.get()) + + if useFlatModalCallsPresentation(context: callController.call.context) { + (self.mainWindow?.viewController as? NavigationController)?.pushViewController(callController) + } else { + self.mainWindow?.present(callController, on: .calls) + } + } else { + self.hasGroupCallOnScreenPromise.set(thisCallIsOnScreenPromise.get()) + + if !call.isIntegratedWithCallKit { + self.callPeerDisposable = (call.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: call.peerId)) + |> deliverOnMainQueue).startStrict(next: { [weak self, weak call] peer in + guard let self, let call, let peer else { + return + } + if self.currentCall != .call(call) { + return + } + + let presentationData = self.currentPresentationData.with({ $0 }) + self.notificationController?.setBlocking(ChatCallNotificationItem( + context: call.context, + strings: presentationData.strings, + nameDisplayOrder: presentationData.nameDisplayOrder, + peer: peer, + isVideo: call.isVideo, + action: { [weak call] answerAction in + guard let call else { + return + } + if answerAction { + self.notificationController?.setBlocking(nil) + call.answer() + } else { + self.notificationController?.setBlocking(nil) + call.rejectBusy() + } + } + )) + }) + } + } + + self.awaitingCallConnectionDisposable = (call.state + |> filter { state in + switch state.state { + case .ringing: + return false + case .terminating, .terminated: + return false + default: + return true + } + } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self, weak callController] _ in + guard let self, let callController, self.callController === callController else { + return + } + self.notificationController?.setBlocking(nil) + + self.callPeerDisposable?.dispose() + self.callPeerDisposable = nil + + thisCallIsOnScreenPromise.set(true) + if useFlatModalCallsPresentation(context: callController.call.context) { + (self.mainWindow?.viewController as? NavigationController)?.pushViewController(callController) + } else { + self.mainWindow?.present(callController, on: .calls) + } + }) + + beginDisplayingCallStatusBar.set(call.state + |> filter { state in + switch state.state { + case .ringing: + return false + case .terminating, .terminated: + return false + default: + return true + } + } + |> take(1) + |> map { _ -> Void in + return Void() + }) + } + + if case let .group(groupCall) = call { + let _ = (makeVoiceChatControllerInitialData(sharedContext: self, accountContext: groupCall.accountContext, call: groupCall) + |> deliverOnMainQueue).start(next: { [weak self, weak transitioningToConferenceCallController] initialData in + guard let self else { + return + } + guard let navigationController = self.mainWindow?.viewController as? NavigationController else { + return + } + + let thisCallIsOnScreenPromise = ValuePromise(false, ignoreRepeated: true) + + let groupCallController = makeVoiceChatController(sharedContext: self, accountContext: groupCall.accountContext, call: groupCall, initialData: initialData, sourceCallController: transitioningToConferenceCallController) + groupCallController.onViewDidAppear = { + thisCallIsOnScreenPromise.set(true) + } + groupCallController.onViewDidDisappear = { + thisCallIsOnScreenPromise.set(false) + } + groupCallController.navigationPresentation = .flatModal + groupCallController.parentNavigationController = navigationController + self.groupCallController = groupCallController + + self.mainWindow?.hostView.containerView.endEditing(true) + + thisCallIsOnScreenPromise.set(true) + self.hasGroupCallOnScreenPromise.set(thisCallIsOnScreenPromise.get()) + beginDisplayingCallStatusBar.set(.single(Void())) + + if let transitioningToConferenceCallController { + transitioningToConferenceCallController.onViewDidAppear = nil + transitioningToConferenceCallController.onViewDidDisappear = nil + } + + if let transitioningToConferenceCallController { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(where: { $0 === transitioningToConferenceCallController }) { + viewControllers.insert(groupCallController, at: index) + navigationController.setViewControllers(viewControllers, animated: false) + viewControllers.remove(at: index + 1) + navigationController.setViewControllers(viewControllers, animated: false) + } else { + navigationController.pushViewController(groupCallController) + } + } else { + navigationController.pushViewController(groupCallController) + } + }) + } + + if self.currentCall != nil { + self.callStateDisposable = (combineLatest(queue: .mainQueue(), + self.hasGroupCallOnScreenPromise.get(), + beginDisplayingCallStatusBar.get() + ) + |> deliverOnMainQueue).startStrict(next: { [weak self] hasGroupCallOnScreen, _ in + guard let self else { + return + } + + var statusBarContent: CallStatusBarNodeImpl.Content? + if !hasGroupCallOnScreen, let currentCall = self.currentCall { + switch currentCall { + case let .call(call): + statusBarContent = .call(self, call.context.account, call) + case let .group(groupCall): + switch groupCall { + case let .conferenceSource(conferenceSource): + statusBarContent = .call(self, conferenceSource.context.account, conferenceSource) + case let .group(groupCall): + statusBarContent = .groupCall(self, groupCall.account, groupCall) + } + } + } + + var resolvedCallStatusBarNode: CallStatusBarNodeImpl? + if let statusBarContent { + if let current = self.currentCallStatusBarNode { + resolvedCallStatusBarNode = current + } else { + resolvedCallStatusBarNode = CallStatusBarNodeImpl() + self.currentCallStatusBarNode = resolvedCallStatusBarNode + } + resolvedCallStatusBarNode?.update(content: statusBarContent) + } else { + self.currentCallStatusBarNode = nil + } + + if let navigationController = self.mainWindow?.viewController as? NavigationController { + navigationController.setForceInCallStatusBar(resolvedCallStatusBarNode) + } + }) + } else { + self.currentCallStatusBarNode = nil + if let navigationController = self.mainWindow?.viewController as? NavigationController { + navigationController.setForceInCallStatusBar(nil) + } + } + } + private func presentControllerWithCurrentCall() { - guard let call = self.call else { + /*guard let call = self.call else { return } @@ -1360,7 +1554,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { } else { self.mainWindow?.present(callController, on: .calls) } - } + }*/ } public func updateNotificationTokensRegistration() { diff --git a/submodules/TelegramUI/Sources/SharedWakeupManager.swift b/submodules/TelegramUI/Sources/SharedWakeupManager.swift index a5ba84a9dd..fca146e0e5 100644 --- a/submodules/TelegramUI/Sources/SharedWakeupManager.swift +++ b/submodules/TelegramUI/Sources/SharedWakeupManager.swift @@ -142,8 +142,16 @@ public final class SharedWakeupManager { |> distinctUntilChanged let hasActiveGroupCalls = (callManager?.currentGroupCallSignal ?? .single(nil)) - |> map { call in - return call?.accountContext.account.id == account.id + |> map { call -> Bool in + guard let call else { + return false + } + switch call { + case let .conferenceSource(conferenceSource): + return conferenceSource.context.account.id == account.id + case let .group(groupCall): + return groupCall.accountContext.account.id == account.id + } } |> distinctUntilChanged diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 72d5365a1a..e1863cd557 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -1328,15 +1328,17 @@ public final class OngoingCallContext { self?.audioLevelPromise.set(.single(level)) } - strongSelf.audioSessionActiveDisposable.set((audioSessionActive - |> deliverOn(queue)).start(next: { isActive in - guard let strongSelf = self else { - return - } - strongSelf.withContext { context in - context.nativeSetIsAudioSessionActive(isActive: isActive) - } - })) + if audioDevice == nil { + strongSelf.audioSessionActiveDisposable.set((audioSessionActive + |> deliverOn(queue)).start(next: { isActive in + guard let strongSelf = self else { + return + } + strongSelf.withContext { context in + context.nativeSetIsAudioSessionActive(isActive: isActive) + } + })) + } strongSelf.networkTypeDisposable = (updatedNetworkType |> deliverOn(queue)).start(next: { networkType in diff --git a/submodules/TgVoipWebrtc/Sources/MediaUtils.h b/submodules/TgVoipWebrtc/Sources/MediaUtils.h index 1bde53a0f2..3044c0204b 100644 --- a/submodules/TgVoipWebrtc/Sources/MediaUtils.h +++ b/submodules/TgVoipWebrtc/Sources/MediaUtils.h @@ -18,7 +18,6 @@ #else #import "platform/darwin/VideoMetalView.h" -#import "platform/darwin/GLVideoView.h" #import "platform/darwin/VideoSampleBufferView.h" #import "platform/darwin/VideoCaptureView.h" #import "platform/darwin/CustomExternalCapturer.h" diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index ea146373e4..c5bc182627 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -71,58 +71,537 @@ public: virtual ~SharedAudioDeviceModule() = default; public: - virtual rtc::scoped_refptr audioDeviceModule() = 0; + virtual rtc::scoped_refptr audioDeviceModule() = 0; + virtual rtc::scoped_refptr makeChildAudioDeviceModule() = 0; virtual void start() = 0; }; } +class WrappedAudioDeviceModuleIOS : public tgcalls::DefaultWrappedAudioDeviceModule, public webrtc::AudioTransport { +public: + WrappedAudioDeviceModuleIOS(webrtc::scoped_refptr impl) : + tgcalls::DefaultWrappedAudioDeviceModule(impl) { + } + + virtual ~WrappedAudioDeviceModuleIOS() { + ActualStop(); + } + + virtual int32_t ActiveAudioLayer(AudioLayer *audioLayer) const override { + return 0; + } + + void UpdateAudioCallback(webrtc::AudioTransport *previousAudioCallback, webrtc::AudioTransport *audioCallback) { + if (audioCallback == nil) { + if (_currentAudioTransport == previousAudioCallback) { + _currentAudioTransport = nil; + } + } else { + _currentAudioTransport = audioCallback; + } + } + + virtual int32_t RegisterAudioCallback(webrtc::AudioTransport *audioCallback) override { + return 0; + } + + virtual int32_t Init() override { + return 0; + } + + virtual int32_t Terminate() override { + return 0; + } + + virtual bool Initialized() const override { + return true; + } + + virtual int16_t PlayoutDevices() override { + return 0; + } + + virtual int16_t RecordingDevices() override { + return 0; + } + + virtual int32_t PlayoutDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], char guid[webrtc::kAdmMaxGuidSize]) override { + return -1; + } + + virtual int32_t RecordingDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], char guid[webrtc::kAdmMaxGuidSize]) override { + return -1; + } + + virtual int32_t SetPlayoutDevice(uint16_t index) override { + return 0; + } + + virtual int32_t SetPlayoutDevice(WindowsDeviceType device) override { + return 0; + } + + virtual int32_t SetRecordingDevice(uint16_t index) override { + return 0; + } + + virtual int32_t SetRecordingDevice(WindowsDeviceType device) override { + return 0; + } + + virtual int32_t PlayoutIsAvailable(bool *available) override { + return 0; + } + + virtual int32_t InitPlayout() override { + return 0; + } + + virtual bool PlayoutIsInitialized() const override { + return true; + } + + virtual int32_t RecordingIsAvailable(bool *available) override { + if (available) { + *available = true; + } + return 0; + } + + virtual int32_t InitRecording() override { + return 0; + } + + virtual bool RecordingIsInitialized() const override { + return true; + } + + virtual int32_t StartPlayout() override { + return 0; + } + + virtual int32_t StopPlayout() override { + return 0; + } + + virtual bool Playing() const override { + return true; + } + + virtual int32_t StartRecording() override { + return 0; + } + + virtual int32_t StopRecording() override { + return 0; + } + + virtual bool Recording() const override { + return true; + } + + virtual int32_t InitSpeaker() override { + return 0; + } + + virtual bool SpeakerIsInitialized() const override { + return true; + } + + virtual int32_t InitMicrophone() override { + return 0; + } + + virtual bool MicrophoneIsInitialized() const override { + return true; + } + + virtual int32_t SpeakerVolumeIsAvailable(bool *available) override { + if (available) { + *available = false; + } + return 0; + } + + virtual int32_t SetSpeakerVolume(uint32_t volume) override { + return 0; + } + + virtual int32_t SpeakerVolume(uint32_t* volume) const override { + if (volume) { + *volume = 0; + } + return 0; + } + + virtual int32_t MaxSpeakerVolume(uint32_t *maxVolume) const override { + if (maxVolume) { + *maxVolume = 0; + } + return 0; + } + + virtual int32_t MinSpeakerVolume(uint32_t *minVolume) const override { + if (minVolume) { + *minVolume = 0; + } + return 0; + } + + virtual int32_t MicrophoneVolumeIsAvailable(bool *available) override { + if (available) { + *available = false; + } + return 0; + } + + virtual int32_t SetMicrophoneVolume(uint32_t volume) override { + return 0; + } + + virtual int32_t MicrophoneVolume(uint32_t *volume) const override { + if (volume) { + *volume = 0; + } + return 0; + } + + virtual int32_t MaxMicrophoneVolume(uint32_t *maxVolume) const override { + if (maxVolume) { + *maxVolume = 0; + } + return 0; + } + + virtual int32_t MinMicrophoneVolume(uint32_t *minVolume) const override { + if (minVolume) { + *minVolume = 0; + } + return 0; + } + + virtual int32_t SpeakerMuteIsAvailable(bool *available) override { + if (available) { + *available = false; + } + return 0; + } + + virtual int32_t SetSpeakerMute(bool enable) override { + return 0; + } + + virtual int32_t SpeakerMute(bool *enabled) const override { + if (enabled) { + *enabled = false; + } + return 0; + } + + virtual int32_t MicrophoneMuteIsAvailable(bool *available) override { + if (available) { + *available = false; + } + return 0; + } + + virtual int32_t SetMicrophoneMute(bool enable) override { + return 0; + } + + virtual int32_t MicrophoneMute(bool *enabled) const override { + if (enabled) { + *enabled = false; + } + return 0; + } + + virtual int32_t StereoPlayoutIsAvailable(bool *available) const override { + if (available) { + *available = false; + } + return 0; + } + + virtual int32_t SetStereoPlayout(bool enable) override { + return 0; + } + + virtual int32_t StereoPlayout(bool *enabled) const override { + if (enabled) { + *enabled = false; + } + return 0; + } + + virtual int32_t StereoRecordingIsAvailable(bool *available) const override { + if (available) { + *available = false; + } + return 0; + } + + virtual int32_t SetStereoRecording(bool enable) override { + return 0; + } + + virtual int32_t StereoRecording(bool *enabled) const override { + if (enabled) { + *enabled = false; + } + return 0; + } + + virtual int32_t PlayoutDelay(uint16_t* delayMS) const override { + if (delayMS) { + *delayMS = 0; + } + return 0; + } + + virtual bool BuiltInAECIsAvailable() const override { + return true; + } + + virtual bool BuiltInAGCIsAvailable() const override { + return true; + } + + virtual bool BuiltInNSIsAvailable() const override { + return true; + } + + virtual int32_t EnableBuiltInAEC(bool enable) override { + return 0; + } + + virtual int32_t EnableBuiltInAGC(bool enable) override { + return 0; + } + + virtual int32_t EnableBuiltInNS(bool enable) override { + return 0; + } + + virtual int32_t GetPlayoutUnderrunCount() const override { + return 0; + } + + virtual int GetPlayoutAudioParameters(webrtc::AudioParameters *params) const override { + return WrappedInstance()->GetPlayoutAudioParameters(params); + } + + virtual int GetRecordAudioParameters(webrtc::AudioParameters *params) const override { + return WrappedInstance()->GetRecordAudioParameters(params); + } + +public: + virtual int32_t RecordedDataIsAvailable( + const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel + ) override { + if (_currentAudioTransport) { + return _currentAudioTransport->RecordedDataIsAvailable( + audioSamples, + nSamples, + nBytesPerSample, + nChannels, + samplesPerSec, + totalDelayMS, + clockDrift, + currentMicLevel, + keyPressed, + newMicLevel + ); + } else { + return 0; + } + } + + virtual int32_t RecordedDataIsAvailable( + const void *audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel, + absl::optional estimatedCaptureTimeNS + ) override { + if (_currentAudioTransport) { + return _currentAudioTransport->RecordedDataIsAvailable( + audioSamples, + nSamples, + nBytesPerSample, + nChannels, + samplesPerSec, + totalDelayMS, + clockDrift, + currentMicLevel, + keyPressed, + newMicLevel, + estimatedCaptureTimeNS + ); + } else { + return 0; + } + } + + // Implementation has to setup safe values for all specified out parameters. + virtual int32_t NeedMorePlayData( + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms + ) override { + if (_currentAudioTransport) { + return _currentAudioTransport->NeedMorePlayData( + nSamples, + nBytesPerSample, + nChannels, + samplesPerSec, + audioSamples, + nSamplesOut, + elapsed_time_ms, + ntp_time_ms + ); + } else { + nSamplesOut = 0; + return 0; + } + } + + virtual void PullRenderData( + int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms + ) override { + if (_currentAudioTransport) { + _currentAudioTransport->PullRenderData( + bits_per_sample, + sample_rate, + number_of_channels, + number_of_frames, + audio_data, + elapsed_time_ms, + ntp_time_ms + ); + } + } + +public: + virtual void Start() { + if (!_isStarted) { + _isStarted = true; + WrappedInstance()->Init(); + + WrappedInstance()->RegisterAudioCallback(this); + + if (!WrappedInstance()->Playing()) { + WrappedInstance()->InitPlayout(); + WrappedInstance()->StartPlayout(); + WrappedInstance()->InitRecording(); + WrappedInstance()->StartRecording(); + } + } + } + + virtual void Stop() override { + } + + virtual void ActualStop() { + if (_isStarted) { + _isStarted = false; + WrappedInstance()->StopPlayout(); + WrappedInstance()->StopRecording(); + WrappedInstance()->Terminate(); + } + } + +private: + bool _isStarted = false; + webrtc::AudioTransport *_currentAudioTransport = nullptr; +}; + +class WrappedChildAudioDeviceModule : public tgcalls::DefaultWrappedAudioDeviceModule { +public: + WrappedChildAudioDeviceModule(webrtc::scoped_refptr impl) : + tgcalls::DefaultWrappedAudioDeviceModule(impl) { + } + + virtual ~WrappedChildAudioDeviceModule() { + } + + virtual int32_t RegisterAudioCallback(webrtc::AudioTransport *audioCallback) override { + auto previousAudioCallback = _audioCallback; + _audioCallback = audioCallback; + + ((WrappedAudioDeviceModuleIOS *)WrappedInstance().get())->UpdateAudioCallback(previousAudioCallback, audioCallback); + + return 0; + } + +private: + webrtc::AudioTransport *_audioCallback = nullptr; +}; + class SharedAudioDeviceModuleImpl: public tgcalls::SharedAudioDeviceModule { public: SharedAudioDeviceModuleImpl(bool disableAudioInput, bool enableSystemMute) { RTC_DCHECK(tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent()); - _audioDeviceModule = rtc::make_ref_counted(false, disableAudioInput, enableSystemMute, disableAudioInput ? 2 : 1); + auto sourceDeviceModule = rtc::make_ref_counted(false, disableAudioInput, enableSystemMute, disableAudioInput ? 2 : 1); + _audioDeviceModule = rtc::make_ref_counted(sourceDeviceModule); } virtual ~SharedAudioDeviceModuleImpl() override { if (tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent()) { - if (_audioDeviceModule->Playing()) { - _audioDeviceModule->StopPlayout(); - _audioDeviceModule->StopRecording(); - } + _audioDeviceModule->ActualStop(); _audioDeviceModule = nullptr; } else { tgcalls::StaticThreads::getThreads()->getWorkerThread()->BlockingCall([&]() { - if (_audioDeviceModule->Playing()) { - _audioDeviceModule->StopPlayout(); - _audioDeviceModule->StopRecording(); - } + _audioDeviceModule->ActualStop(); _audioDeviceModule = nullptr; }); } } public: - virtual rtc::scoped_refptr audioDeviceModule() override { + virtual rtc::scoped_refptr audioDeviceModule() override { return _audioDeviceModule; } + rtc::scoped_refptr makeChildAudioDeviceModule() override { + return rtc::make_ref_counted(_audioDeviceModule); + } + virtual void start() override { RTC_DCHECK(tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent()); - _audioDeviceModule->Init(); - if (!_audioDeviceModule->Playing()) { - _audioDeviceModule->InitPlayout(); - //_audioDeviceModule->InitRecording(); - if (_audioDeviceModule->PlayoutIsInitialized()) { - _audioDeviceModule->InternalStartPlayout(); - } - //_audioDeviceModule->InternalStartRecording(); - } + _audioDeviceModule->Start(); } private: - rtc::scoped_refptr _audioDeviceModule; + rtc::scoped_refptr _audioDeviceModule; }; @implementation SharedCallAudioDevice { @@ -145,7 +624,8 @@ private: - (void)setTone:(CallAudioTone * _Nullable)tone { _audioDeviceModule->perform([tone](tgcalls::SharedAudioDeviceModule *audioDeviceModule) { - audioDeviceModule->audioDeviceModule()->setTone([tone asTone]); + //audioDeviceModule->audioDeviceModule()->setTone([tone asTone]); + //TODO:implement }); } @@ -1303,6 +1783,13 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; return resultModule; } }, + .createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr { + if (audioDeviceModule) { + return audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule(); + } else { + return nullptr; + } + }, .directConnectionChannel = directConnectionChannel }); _state = OngoingCallStateInitializing; @@ -2000,6 +2487,13 @@ isConference:(bool)isConference { return resultModule; } }, + .createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr { + if (audioDeviceModule) { + return audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule(); + } else { + return nullptr; + } + }, .onMutedSpeechActivityDetected = [weakSelf, queue](bool value) { [queue dispatch:^{ __strong GroupCallThreadLocalContext *strongSelf = weakSelf; diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 421d23ead1..b07cb07fb9 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 421d23ead1cf8595fb72087a01d7b1452ffd2723 +Subproject commit b07cb07fb9bcb97f745e74c27f8751e8bef1dfb3