mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Conference calls
This commit is contained in:
parent
0648ffbb2a
commit
3e74304640
@ -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 {
|
public protocol PresentationCallManager: AnyObject {
|
||||||
var currentCallSignal: Signal<PresentationCall?, NoError> { get }
|
var currentCallSignal: Signal<PresentationCall?, NoError> { get }
|
||||||
var currentGroupCallSignal: Signal<PresentationGroupCall?, NoError> { get }
|
var currentGroupCallSignal: Signal<VideoChatCall?, NoError> { get }
|
||||||
var hasActiveCall: Bool { get }
|
var hasActiveCall: Bool { get }
|
||||||
var hasActiveGroupCall: Bool { get }
|
var hasActiveGroupCall: Bool { get }
|
||||||
|
|
||||||
|
@ -466,7 +466,10 @@ final class CallListControllerNode: ASDisplayNode {
|
|||||||
if let callManager = context.sharedContext.callManager {
|
if let callManager = context.sharedContext.callManager {
|
||||||
currentGroupCallPeerId = callManager.currentGroupCallSignal
|
currentGroupCallPeerId = callManager.currentGroupCallSignal
|
||||||
|> map { call -> EnginePeer.Id? in
|
|> map { call -> EnginePeer.Id? in
|
||||||
call?.peerId
|
guard case let .group(call) = call else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return call.peerId
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
} else {
|
} else {
|
||||||
|
@ -77,12 +77,34 @@ private final class GlobalOverlayContainerParent: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class NavigationControllerNode: 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?
|
private weak var controller: NavigationController?
|
||||||
|
|
||||||
init(controller: NavigationController) {
|
init(controller: NavigationController) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.setViewBlock({
|
||||||
|
return View(frame: CGRect())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
@ -100,6 +122,10 @@ private final class NavigationControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func schedule(layout f: @escaping () -> Void) {
|
||||||
|
(self.view as? View)?.schedule(layout: f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol NavigationControllerDropContentItem: AnyObject {
|
public protocol NavigationControllerDropContentItem: AnyObject {
|
||||||
@ -376,6 +402,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
self.loadView()
|
self.loadView()
|
||||||
}
|
}
|
||||||
self.validLayout = layout
|
self.validLayout = layout
|
||||||
|
|
||||||
|
self.scheduledLayoutTransitionRequest = nil
|
||||||
|
|
||||||
self.updateContainers(layout: layout, transition: transition)
|
self.updateContainers(layout: layout, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1805,18 +1834,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func scheduleAfterLayout(_ f: @escaping () -> Void) {
|
|
||||||
(self.view as? UITracingLayerView)?.schedule(layout: {
|
|
||||||
f()
|
|
||||||
})
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func scheduleLayoutTransitionRequest(_ transition: ContainedViewLayoutTransition) {
|
private func scheduleLayoutTransitionRequest(_ transition: ContainedViewLayoutTransition) {
|
||||||
let requestId = self.scheduledLayoutTransitionRequestId
|
let requestId = self.scheduledLayoutTransitionRequestId
|
||||||
self.scheduledLayoutTransitionRequestId += 1
|
self.scheduledLayoutTransitionRequestId += 1
|
||||||
self.scheduledLayoutTransitionRequest = (requestId, transition)
|
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 strongSelf = self {
|
||||||
if let (currentRequestId, currentRequestTransition) = strongSelf.scheduledLayoutTransitionRequest, currentRequestId == requestId {
|
if let (currentRequestId, currentRequestTransition) = strongSelf.scheduledLayoutTransitionRequest, currentRequestId == requestId {
|
||||||
strongSelf.scheduledLayoutTransitionRequest = nil
|
strongSelf.scheduledLayoutTransitionRequest = nil
|
||||||
@ -1869,7 +1891,8 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
}
|
}
|
||||||
if let layout = self.validLayout {
|
if let layout = self.validLayout {
|
||||||
inCallStatusBar.updateState(statusBar: nil, withSafeInsets: !layout.safeInsets.top.isZero, inCallNode: forceInCallStatusBar, animated: false)
|
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 {
|
} else {
|
||||||
self.updateInCallStatusBarState = forceInCallStatusBar
|
self.updateInCallStatusBarState = forceInCallStatusBar
|
||||||
}
|
}
|
||||||
@ -1879,7 +1902,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
inCallStatusBar?.removeFromSupernode()
|
inCallStatusBar?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
if let layout = self.validLayout {
|
if let layout = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout, transition: transition)
|
let _ = layout
|
||||||
|
self.scheduleLayoutTransitionRequest(transition)
|
||||||
|
//self.containerLayoutUpdated(layout, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,10 +260,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
let currentGroupCall: Signal<PresentationGroupCall?, NoError> = callManager.currentGroupCallSignal
|
let currentGroupCall: Signal<PresentationGroupCall?, NoError> = callManager.currentGroupCallSignal
|
||||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
return lhs?.internalId == rhs?.internalId
|
return lhs == rhs
|
||||||
})
|
})
|
||||||
|> map { call -> PresentationGroupCall? in
|
|> 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 nil
|
||||||
}
|
}
|
||||||
return call
|
return call
|
||||||
|
@ -62,6 +62,15 @@ final class SharedCallAudioContext {
|
|||||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||||
audioSessionControl.setup(synchronous: true)
|
audioSessionControl.setup(synchronous: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let audioSessionActive: Signal<Bool, NoError>
|
||||||
|
if let callKitIntegration = self.callKitIntegration {
|
||||||
|
audioSessionActive = callKitIntegration.audioSessionActive
|
||||||
|
} else {
|
||||||
|
audioSessionControl.activate({ _ in })
|
||||||
|
audioSessionActive = .single(true)
|
||||||
|
}
|
||||||
|
self.isAudioSessionActivePromise.set(audioSessionActive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, deactivate: { [weak self] _ in
|
}, deactivate: { [weak self] _ in
|
||||||
@ -130,7 +139,7 @@ final class SharedCallAudioContext {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = self
|
self.audioDevice?.setIsAudioSessionActive(value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +61,11 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
private var currentCallDisposable = MetaDisposable()
|
private var currentCallDisposable = MetaDisposable()
|
||||||
private let removeCurrentCallDisposable = MetaDisposable()
|
private let removeCurrentCallDisposable = MetaDisposable()
|
||||||
private let removeCurrentGroupCallDisposable = MetaDisposable()
|
private let removeCurrentGroupCallDisposable = MetaDisposable()
|
||||||
|
private var callToConferenceDisposable: Disposable?
|
||||||
|
private var currentUpgradedToConferenceCallId: CallSessionInternalId?
|
||||||
|
|
||||||
private var currentGroupCallValue: PresentationGroupCallImpl?
|
private var currentGroupCallValue: VideoChatCall?
|
||||||
private var currentGroupCall: PresentationGroupCallImpl? {
|
private var currentGroupCall: VideoChatCall? {
|
||||||
return self.currentGroupCallValue
|
return self.currentGroupCallValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,8 +97,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
return self.currentCallPromise.get()
|
return self.currentCallPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private let currentGroupCallPromise = Promise<PresentationGroupCall?>(nil)
|
private let currentGroupCallPromise = Promise<VideoChatCall?>(nil)
|
||||||
public var currentGroupCallSignal: Signal<PresentationGroupCall?, NoError> {
|
public var currentGroupCallSignal: Signal<VideoChatCall?, NoError> {
|
||||||
return self.currentGroupCallPromise.get()
|
return self.currentGroupCallPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,16 +299,23 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
self.startCallDisposable.dispose()
|
self.startCallDisposable.dispose()
|
||||||
self.proxyServerDisposable?.dispose()
|
self.proxyServerDisposable?.dispose()
|
||||||
self.callSettingsDisposable?.dispose()
|
self.callSettingsDisposable?.dispose()
|
||||||
|
self.callToConferenceDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func ringingStatesUpdated(_ ringingStates: [(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
|
private func ringingStatesUpdated(_ ringingStates: [(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
|
||||||
if let firstState = ringingStates.first {
|
if let firstState = ringingStates.first {
|
||||||
if self.currentCall == nil && self.currentGroupCall == nil {
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] preferences, sharedData in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if strongSelf.currentUpgradedToConferenceCallId == firstState.2.id {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let configuration = preferences.values[PreferencesKeys.voipConfiguration]?.get(VoipConfiguration.self) ?? .defaultValue
|
let configuration = preferences.values[PreferencesKeys.voipConfiguration]?.get(VoipConfiguration.self) ?? .defaultValue
|
||||||
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings]?.get(AutodownloadSettings.self) ?? .defaultSettings
|
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings]?.get(AutodownloadSettings.self) ?? .defaultSettings
|
||||||
@ -338,18 +347,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
||||||
)
|
)
|
||||||
strongSelf.updateCurrentCall(call)
|
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 {
|
} else {
|
||||||
for (context, _, state, _, _) in ringingStates {
|
for (context, _, state, _, _) in ringingStates {
|
||||||
@ -370,12 +367,15 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
alreadyInCallWithPeerId = call.peerId
|
alreadyInCallWithPeerId = call.peerId
|
||||||
} else if let currentGroupCall = self.currentGroupCallValue {
|
} else if let currentGroupCall = self.currentGroupCallValue {
|
||||||
alreadyInCall = true
|
alreadyInCall = true
|
||||||
alreadyInCallWithPeerId = currentGroupCall.peerId
|
switch currentGroupCall {
|
||||||
|
case let .conferenceSource(conferenceSource):
|
||||||
|
alreadyInCallWithPeerId = conferenceSource.peerId
|
||||||
|
case let .group(groupCall):
|
||||||
|
alreadyInCallWithPeerId = groupCall.peerId
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if #available(iOS 10.0, *) {
|
if CXCallObserver().calls.contains(where: { $0.hasEnded == false }) {
|
||||||
if CXCallObserver().calls.contains(where: { $0.hasEnded == false }) {
|
alreadyInCall = true
|
||||||
alreadyInCall = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,12 +456,22 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
begin()
|
begin()
|
||||||
}))
|
}))
|
||||||
} else if let currentGroupCall = self.currentGroupCallValue {
|
} else if let currentGroupCall = self.currentGroupCallValue {
|
||||||
self.startCallDisposable.set((currentGroupCall.leave(terminateIfPossible: false)
|
switch currentGroupCall {
|
||||||
|> filter { $0 }
|
case let .conferenceSource(conferenceSource):
|
||||||
|> take(1)
|
self.startCallDisposable.set((conferenceSource.hangUp()
|
||||||
|> deliverOnMainQueue).start(next: { _ in
|
|> filter { $0 }
|
||||||
begin()
|
|> 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 {
|
} else {
|
||||||
begin()
|
begin()
|
||||||
}
|
}
|
||||||
@ -478,12 +488,22 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
begin()
|
begin()
|
||||||
}))
|
}))
|
||||||
} else if let currentGroupCall = self.currentGroupCallValue {
|
} else if let currentGroupCall = self.currentGroupCallValue {
|
||||||
self.startCallDisposable.set((currentGroupCall.leave(terminateIfPossible: false)
|
switch currentGroupCall {
|
||||||
|> filter { $0 }
|
case let .conferenceSource(conferenceSource):
|
||||||
|> take(1)
|
self.startCallDisposable.set((conferenceSource.hangUp()
|
||||||
|> deliverOnMainQueue).start(next: { _ in
|
|> filter { $0 }
|
||||||
begin()
|
|> 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 {
|
} else {
|
||||||
begin()
|
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 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 (combineLatest(queue: .mainQueue(),
|
||||||
return peerView.peerIsContact
|
request,
|
||||||
} |> take(1), context.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), areVideoCallsAvailable)
|
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
|
|> deliverOnMainQueue
|
||||||
|> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, areVideoCallsAvailable in
|
|> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, areVideoCallsAvailable in
|
||||||
if let strongSelf = self, accessEnabled {
|
if let strongSelf = self, accessEnabled {
|
||||||
@ -586,18 +615,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
||||||
)
|
)
|
||||||
strongSelf.updateCurrentCall(call)
|
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<Bool, NoError> in
|
|> mapToSignal { value -> Signal<Bool, NoError> in
|
||||||
@ -613,7 +630,48 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
self.resumeMedia = self.isMediaPlaying()
|
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 {
|
if !wasEmpty && isEmpty && self.resumeMedia {
|
||||||
self.resumeMedia = false
|
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 wasEmpty = self.currentGroupCallValue == nil
|
||||||
let isEmpty = value == nil
|
let isEmpty = value == nil
|
||||||
if wasEmpty && !isEmpty {
|
if wasEmpty && !isEmpty {
|
||||||
self.resumeMedia = self.isMediaPlaying()
|
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 {
|
if !wasEmpty && isEmpty && self.resumeMedia {
|
||||||
self.resumeMedia = false
|
self.resumeMedia = false
|
||||||
@ -671,7 +791,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
)
|
)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> mapToSignal { [weak self, weak parentController] accessEnabled, peer -> Signal<Bool, NoError> in
|
|> mapToSignal { [weak self, weak parentController] accessEnabled, peer -> Signal<Bool, NoError> in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return .single(false)
|
return .single(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,93 +804,36 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
isChannel = true
|
isChannel = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldUseV2VideoChatImpl(context: accountContext) {
|
if let parentController {
|
||||||
if let parentController {
|
parentController.push(ScheduleVideoChatSheetScreen(
|
||||||
parentController.push(ScheduleVideoChatSheetScreen(
|
context: accountContext,
|
||||||
context: accountContext,
|
scheduleAction: { [weak self] timestamp in
|
||||||
scheduleAction: { timestamp in
|
guard let self else {
|
||||||
guard let self else {
|
return
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
))
|
|
||||||
}
|
let call = PresentationGroupCallImpl(
|
||||||
|
accountContext: accountContext,
|
||||||
return .single(true)
|
audioSession: self.audioSession,
|
||||||
} else {
|
callKitIntegration: nil,
|
||||||
let call = PresentationGroupCallImpl(
|
getDeviceAccessData: self.getDeviceAccessData,
|
||||||
accountContext: accountContext,
|
initialCall: nil,
|
||||||
audioSession: strongSelf.audioSession,
|
internalId: internalId,
|
||||||
callKitIntegration: nil,
|
peerId: peerId,
|
||||||
getDeviceAccessData: strongSelf.getDeviceAccessData,
|
isChannel: isChannel,
|
||||||
initialCall: nil,
|
invite: nil,
|
||||||
internalId: internalId,
|
joinAsPeerId: nil,
|
||||||
peerId: peerId,
|
isStream: false,
|
||||||
isChannel: isChannel,
|
encryptionKey: nil,
|
||||||
invite: nil,
|
conferenceFromCallId: nil,
|
||||||
joinAsPeerId: nil,
|
isConference: false,
|
||||||
isStream: false,
|
sharedAudioContext: nil
|
||||||
encryptionKey: nil,
|
)
|
||||||
conferenceFromCallId: nil,
|
call.schedule(timestamp: timestamp)
|
||||||
isConference: false,
|
|
||||||
sharedAudioContext: nil
|
self.updateCurrentGroupCall(.group(call))
|
||||||
)
|
|
||||||
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)
|
return .single(true)
|
||||||
@ -787,15 +850,29 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
|
|
||||||
if let currentGroupCall = self.currentGroupCallValue {
|
if let currentGroupCall = self.currentGroupCallValue {
|
||||||
if endCurrentIfAny {
|
if endCurrentIfAny {
|
||||||
let endSignal = currentGroupCall.leave(terminateIfPossible: false)
|
switch currentGroupCall {
|
||||||
|> filter { $0 }
|
case let .conferenceSource(conferenceSource):
|
||||||
|> take(1)
|
self.startCallDisposable.set((conferenceSource.hangUp()
|
||||||
|> deliverOnMainQueue
|
|> filter { $0 }
|
||||||
self.startCallDisposable.set(endSignal.start(next: { _ in
|
|> take(1)
|
||||||
begin()
|
|> 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 {
|
} 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 {
|
} else if let currentCall = self.currentCall {
|
||||||
if endCurrentIfAny {
|
if endCurrentIfAny {
|
||||||
@ -832,15 +909,29 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
|
|
||||||
if let currentGroupCall = self.currentGroupCallValue {
|
if let currentGroupCall = self.currentGroupCallValue {
|
||||||
if endCurrentIfAny {
|
if endCurrentIfAny {
|
||||||
let endSignal = currentGroupCall.leave(terminateIfPossible: false)
|
switch currentGroupCall {
|
||||||
|> filter { $0 }
|
case let .conferenceSource(conferenceSource):
|
||||||
|> take(1)
|
self.startCallDisposable.set((conferenceSource.hangUp()
|
||||||
|> deliverOnMainQueue
|
|> filter { $0 }
|
||||||
self.startCallDisposable.set(endSignal.start(next: { _ in
|
|> take(1)
|
||||||
begin()
|
|> 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 {
|
} 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 {
|
} else if let currentCall = self.currentCall {
|
||||||
if endCurrentIfAny {
|
if endCurrentIfAny {
|
||||||
@ -919,48 +1010,51 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
return .single(false)
|
return .single(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var isChannel = false
|
strongSelf.createGroupCall(
|
||||||
if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info {
|
|
||||||
isChannel = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let call = PresentationGroupCallImpl(
|
|
||||||
accountContext: accountContext,
|
accountContext: accountContext,
|
||||||
audioSession: strongSelf.audioSession,
|
peerId: peerId,
|
||||||
callKitIntegration: nil,
|
peer: peer,
|
||||||
getDeviceAccessData: strongSelf.getDeviceAccessData,
|
|
||||||
initialCall: initialCall,
|
initialCall: initialCall,
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
peerId: peerId,
|
|
||||||
isChannel: isChannel,
|
|
||||||
invite: invite,
|
invite: invite,
|
||||||
joinAsPeerId: joinAsPeerId,
|
joinAsPeerId: joinAsPeerId
|
||||||
isStream: initialCall.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
|
|
||||||
}
|
|
||||||
if value {
|
|
||||||
if strongSelf.currentGroupCall === call {
|
|
||||||
strongSelf.updateCurrentGroupCall(nil)
|
|
||||||
strongSelf.currentGroupCallPromise.set(.single(nil))
|
|
||||||
strongSelf.hasActiveGroupCallsPromise.set(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
return .single(true)
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1111,6 +1111,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.conferenceFromCallId = conferenceFromCallId
|
self.conferenceFromCallId = conferenceFromCallId
|
||||||
self.isConference = isConference
|
self.isConference = isConference
|
||||||
self.encryptionKey = encryptionKey
|
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
|
self.sharedAudioContext = sharedAudioContext
|
||||||
|
|
||||||
if self.sharedAudioContext == nil && !accountContext.sharedContext.immediateExperimentalUISettings.liveStreamV2 {
|
if self.sharedAudioContext == nil && !accountContext.sharedContext.immediateExperimentalUISettings.liveStreamV2 {
|
||||||
@ -1906,7 +1913,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
var encryptionKey: Data?
|
var encryptionKey: Data?
|
||||||
encryptionKey = self.encryptionKey?.key
|
encryptionKey = self.encryptionKey?.key
|
||||||
encryptionKey = nil
|
|
||||||
|
|
||||||
let contextAudioSessionActive: Signal<Bool, NoError>
|
let contextAudioSessionActive: Signal<Bool, NoError>
|
||||||
if self.sharedAudioContext != nil {
|
if self.sharedAudioContext != nil {
|
||||||
@ -2875,18 +2881,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
let _ = (callManager.currentGroupCallSignal
|
let _ = (callManager.currentGroupCallSignal
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] call in
|
|> deliverOnMainQueue).start(next: { [weak self] call in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let call = call, call !== strongSelf {
|
if let call = call, call != .group(self) {
|
||||||
strongSelf.wasRemoved.set(.single(true))
|
self.wasRemoved.set(.single(true))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.beginTone(tone: .groupLeft)
|
self.beginTone(tone: .groupLeft)
|
||||||
|
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(1.0, {
|
||||||
strongSelf.wasRemoved.set(.single(true))
|
self.wasRemoved.set(.single(true))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -24,16 +24,7 @@ import TelegramAudio
|
|||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import TooltipUI
|
import TooltipUI
|
||||||
|
|
||||||
extension VideoChatCall {
|
extension VideoChatCall {
|
||||||
var accountContext: AccountContext {
|
|
||||||
switch self {
|
|
||||||
case let .group(group):
|
|
||||||
return group.accountContext
|
|
||||||
case let .conferenceSource(conferenceSource):
|
|
||||||
return conferenceSource.context
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
|
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
|
||||||
switch self {
|
switch self {
|
||||||
case let .group(group):
|
case let .group(group):
|
||||||
|
@ -98,7 +98,7 @@ public final class VoiceChatJoinScreen: ViewController {
|
|||||||
currentGroupCall = callManager.currentGroupCallSignal
|
currentGroupCall = callManager.currentGroupCallSignal
|
||||||
|> castError(GetCurrentGroupCallError.self)
|
|> castError(GetCurrentGroupCallError.self)
|
||||||
|> mapToSignal { call -> Signal<(PresentationGroupCall, Int64, Bool)?, GetCurrentGroupCallError> in
|
|> mapToSignal { call -> Signal<(PresentationGroupCall, Int64, Bool)?, GetCurrentGroupCallError> in
|
||||||
if let call = call {
|
if case let .group(call) = call {
|
||||||
return call.summaryState
|
return call.summaryState
|
||||||
|> castError(GetCurrentGroupCallError.self)
|
|> castError(GetCurrentGroupCallError.self)
|
||||||
|> map { state -> (PresentationGroupCall, Int64, Bool)? in
|
|> map { state -> (PresentationGroupCall, Int64, Bool)? in
|
||||||
|
@ -93,7 +93,7 @@ final class KeyEmojiView: HighlightTrackingButton {
|
|||||||
for i in 0 ..< self.emojiViews.count {
|
for i in 0 ..< self.emojiViews.count {
|
||||||
let emojiView = self.emojiViews[i]
|
let emojiView = self.emojiViews[i]
|
||||||
emojiView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
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 {
|
private func update(params: Params, transition: ComponentTransition) -> CGSize {
|
||||||
let itemSpacing: CGFloat = 1.0
|
let itemSpacing: CGFloat = 0.0
|
||||||
|
|
||||||
var height: CGFloat = 0.0
|
var height: CGFloat = 0.0
|
||||||
var nextX = 0.0
|
var nextX = 0.0
|
||||||
@ -118,7 +118,7 @@ final class KeyEmojiView: HighlightTrackingButton {
|
|||||||
nextX += itemSpacing
|
nextX += itemSpacing
|
||||||
}
|
}
|
||||||
let emojiView = self.emojiViews[i]
|
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 {
|
if height == 0.0 {
|
||||||
height = itemSize.height
|
height = itemSize.height
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to targetPosit
|
|||||||
let numPoints: Int = Int(ceil(Double(UIScreen.main.maximumFramesPerSecond) * duration))
|
let numPoints: Int = Int(ceil(Double(UIScreen.main.maximumFramesPerSecond) * duration))
|
||||||
|
|
||||||
var keyframes: [CGPoint] = []
|
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 {
|
for rawI in 0 ..< numPoints {
|
||||||
let i = reverse ? (numPoints - 1 - rawI) : rawI
|
let i = reverse ? (numPoints - 1 - rawI) : rawI
|
||||||
let ks = CGFloat(i) / CGFloat(numPoints - 1)
|
let ks = CGFloat(i) / CGFloat(numPoints - 1)
|
||||||
|
@ -1241,8 +1241,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
|||||||
|
|
||||||
let titleSize = self.titleView.update(
|
let titleSize = self.titleView.update(
|
||||||
string: titleString,
|
string: titleString,
|
||||||
fontSize: !havePrimaryVideo ? 28.0 : 17.0,
|
fontSize: 28.0,
|
||||||
fontWeight: !havePrimaryVideo ? 0.0 : 0.25,
|
fontWeight: 0.0,
|
||||||
color: .white,
|
color: .white,
|
||||||
constrainedWidth: params.size.width - 16.0 * 2.0,
|
constrainedWidth: params.size.width - 16.0 * 2.0,
|
||||||
transition: transition
|
transition: transition
|
||||||
@ -1301,7 +1301,11 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
|||||||
if currentAreControlsHidden {
|
if currentAreControlsHidden {
|
||||||
titleY = -8.0 - titleSize.height - statusSize.height
|
titleY = -8.0 - titleSize.height - statusSize.height
|
||||||
} else if havePrimaryVideo {
|
} 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 {
|
} else {
|
||||||
titleY = collapsedAvatarFrame.maxY + 39.0
|
titleY = collapsedAvatarFrame.maxY + 39.0
|
||||||
}
|
}
|
||||||
@ -1313,7 +1317,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
|||||||
size: titleSize
|
size: titleSize
|
||||||
)
|
)
|
||||||
transition.setFrame(view: self.titleView, frame: titleFrame)
|
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 emojiViewSizeValue: CGSize?
|
||||||
var emojiTransition = transition
|
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(
|
let statusFrame = CGRect(
|
||||||
origin: CGPoint(
|
origin: CGPoint(
|
||||||
x: (params.size.width - statusSize.width - statusEmojiWidth) * 0.5,
|
x: (params.size.width - statusSize.width) * 0.5,
|
||||||
y: titleFrame.maxY + (havePrimaryVideo ? 0.0 : 4.0)
|
y: titleFrame.maxY + 4.0
|
||||||
),
|
),
|
||||||
size: statusSize
|
size: statusSize
|
||||||
)
|
)
|
||||||
@ -1414,12 +1413,19 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
transition.setFrame(view: self.statusView, frame: statusFrame)
|
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 case .active = params.state.lifecycleState {
|
||||||
if let emojiView = self.emojiView, !self.isEmojiKeyExpanded, let emojiViewSize = emojiViewSizeValue {
|
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 {
|
if case let .curve(duration, curve) = transition.animation, emojiViewWasExpanded {
|
||||||
let distance = CGPoint(x: emojiViewFrame.midX - emojiView.center.x, y: emojiViewFrame.midY - emojiView.center.y)
|
let distance = CGPoint(x: emojiViewFrame.midX - emojiView.center.x, y: emojiViewFrame.midY - emojiView.center.y)
|
||||||
|
@ -163,17 +163,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
public var callManager: PresentationCallManager?
|
public var callManager: PresentationCallManager?
|
||||||
let hasInAppPurchases: Bool
|
let hasInAppPurchases: Bool
|
||||||
|
|
||||||
private var callDisposable: Disposable?
|
|
||||||
private var callStateDisposable: Disposable?
|
private var callStateDisposable: Disposable?
|
||||||
|
|
||||||
private(set) var currentCallStatusBarNode: CallStatusBarNodeImpl?
|
private(set) var currentCallStatusBarNode: CallStatusBarNodeImpl?
|
||||||
|
|
||||||
|
private var callDisposable: Disposable?
|
||||||
private var groupCallDisposable: Disposable?
|
private var groupCallDisposable: Disposable?
|
||||||
|
|
||||||
private var callController: CallController?
|
private var callController: CallController?
|
||||||
private var call: PresentationCall?
|
|
||||||
|
private var currentCall: PresentationCurrentCall?
|
||||||
|
|
||||||
public let hasOngoingCall = ValuePromise<Bool>(false)
|
public let hasOngoingCall = ValuePromise<Bool>(false)
|
||||||
private let callState = Promise<PresentationCallState?>(nil)
|
|
||||||
private var awaitingCallConnectionDisposable: Disposable?
|
private var awaitingCallConnectionDisposable: Disposable?
|
||||||
private var callPeerDisposable: Disposable?
|
private var callPeerDisposable: Disposable?
|
||||||
private var callIsConferenceDisposable: Disposable?
|
private var callIsConferenceDisposable: Disposable?
|
||||||
@ -182,7 +183,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
public var currentGroupCallController: ViewController? {
|
public var currentGroupCallController: ViewController? {
|
||||||
return self.groupCallController
|
return self.groupCallController
|
||||||
}
|
}
|
||||||
private let hasGroupCallOnScreenPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let hasGroupCallOnScreenPromise = Promise<Bool>(false)
|
||||||
public var hasGroupCallOnScreen: Signal<Bool, NoError> {
|
public var hasGroupCallOnScreen: Signal<Bool, NoError> {
|
||||||
return self.hasGroupCallOnScreenPromise.get()
|
return self.hasGroupCallOnScreenPromise.get()
|
||||||
}
|
}
|
||||||
@ -805,8 +806,14 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
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
|
let previousCall = self.call
|
||||||
self.call = call
|
self.call = call
|
||||||
|
|
||||||
@ -842,11 +849,6 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.callIsConferenceDisposable = nil
|
self.callIsConferenceDisposable = nil
|
||||||
|
|
||||||
if let call {
|
if let call {
|
||||||
if call.conferenceStateValue == nil && call.conferenceCall == nil {
|
|
||||||
self.callState.set(call.state
|
|
||||||
|> map(Optional.init))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.hasOngoingCall.set(true)
|
self.hasOngoingCall.set(true)
|
||||||
setNotificationCall(call)
|
setNotificationCall(call)
|
||||||
|
|
||||||
@ -862,14 +864,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
guard let callController = self.callController, callController.call === call else {
|
guard let callController = self.callController, callController.call === call else {
|
||||||
if self.callController == nil, call.conferenceStateValue != nil {
|
if self.callController == nil, call.conferenceStateValue != nil {
|
||||||
self.callState.set(.single(nil))
|
|
||||||
self.presentControllerWithCurrentCall()
|
self.presentControllerWithCurrentCall()
|
||||||
self.notificationController?.setBlocking(nil)
|
self.notificationController?.setBlocking(nil)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if call.conferenceStateValue != nil {
|
if call.conferenceStateValue != nil {
|
||||||
self.callState.set(.single(nil))
|
|
||||||
self.presentControllerWithCurrentCall()
|
self.presentControllerWithCurrentCall()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -932,168 +932,99 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.awaitingCallConnectionDisposable = nil
|
self.awaitingCallConnectionDisposable = nil
|
||||||
setNotificationCall(nil)
|
setNotificationCall(nil)
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
})
|
})
|
||||||
|
|
||||||
self.groupCallDisposable = (callManager.currentGroupCallSignal
|
self.groupCallDisposable = (callManager.currentGroupCallSignal
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] call in
|
|> deliverOnMainQueue).start(next: { [weak self] call in
|
||||||
if let strongSelf = self {
|
guard let self else {
|
||||||
if call.flatMap(VideoChatCall.group) != strongSelf.groupCallController?.call {
|
return
|
||||||
strongSelf.groupCallController?.dismiss(closing: true, manual: false)
|
}
|
||||||
strongSelf.groupCallController = nil
|
|
||||||
strongSelf.hasOngoingCall.set(false)
|
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 {
|
if call.isStream {
|
||||||
mainWindow.hostView.containerView.endEditing(true)
|
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 {
|
let _ = (makeVoiceChatControllerInitialData(sharedContext: self, accountContext: call.accountContext, call: .group(call))
|
||||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
|> deliverOnMainQueue).start(next: { [weak self, weak navigationController] initialData in
|
||||||
let groupCallController = MediaStreamComponentController(call: call)
|
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
|
groupCallController.onViewDidAppear = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
self.hasGroupCallOnScreenPromise.set(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
groupCallController.onViewDidDisappear = { [weak self] in
|
groupCallController.onViewDidDisappear = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.hasGroupCallOnScreenPromise.set(false)
|
self.hasGroupCallOnScreenPromise.set(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
groupCallController.navigationPresentation = .flatModal
|
groupCallController.navigationPresentation = .flatModal
|
||||||
groupCallController.parentNavigationController = navigationController
|
groupCallController.parentNavigationController = navigationController
|
||||||
strongSelf.groupCallController = groupCallController
|
strongSelf.groupCallController = groupCallController
|
||||||
navigationController.pushViewController(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)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
self.hasOngoingCall.set(true)
|
||||||
})
|
|
||||||
|
|
||||||
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<PresentationGroupCall?, NoError> = .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)
|
|
||||||
} else {
|
} 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
|
mainWindow.inCallNavigate = { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let callController = strongSelf.callController {
|
if let callController = self.callController {
|
||||||
if callController.isNodeLoaded {
|
mainWindow.hostView.containerView.endEditing(true)
|
||||||
mainWindow.hostView.containerView.endEditing(true)
|
if callController.view.superview == nil {
|
||||||
if callController.view.superview == nil {
|
if useFlatModalCallsPresentation(context: callController.call.context) {
|
||||||
if useFlatModalCallsPresentation(context: callController.call.context) {
|
(mainWindow.viewController as? NavigationController)?.pushViewController(callController)
|
||||||
(mainWindow.viewController as? NavigationController)?.pushViewController(callController)
|
|
||||||
} else {
|
|
||||||
mainWindow.present(callController, on: .calls)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
callController.expandFromPipIfPossible()
|
mainWindow.present(callController, on: .calls)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
callController.expandFromPipIfPossible()
|
||||||
}
|
}
|
||||||
} else if let groupCallController = strongSelf.groupCallController {
|
} else if let groupCallController = self.groupCallController {
|
||||||
if groupCallController.isNodeLoaded {
|
mainWindow.hostView.containerView.endEditing(true)
|
||||||
mainWindow.hostView.containerView.endEditing(true)
|
if groupCallController.view.superview == nil {
|
||||||
if groupCallController.view.superview == nil {
|
(mainWindow.viewController as? NavigationController)?.pushViewController(groupCallController)
|
||||||
(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<Void>()
|
||||||
|
|
||||||
|
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<Bool>(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<Bool>(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() {
|
private func presentControllerWithCurrentCall() {
|
||||||
guard let call = self.call else {
|
/*guard let call = self.call else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1360,7 +1554,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
} else {
|
} else {
|
||||||
self.mainWindow?.present(callController, on: .calls)
|
self.mainWindow?.present(callController, on: .calls)
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateNotificationTokensRegistration() {
|
public func updateNotificationTokensRegistration() {
|
||||||
|
@ -142,8 +142,16 @@ public final class SharedWakeupManager {
|
|||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
|
||||||
let hasActiveGroupCalls = (callManager?.currentGroupCallSignal ?? .single(nil))
|
let hasActiveGroupCalls = (callManager?.currentGroupCallSignal ?? .single(nil))
|
||||||
|> map { call in
|
|> map { call -> Bool in
|
||||||
return call?.accountContext.account.id == account.id
|
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
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
@ -1328,15 +1328,17 @@ public final class OngoingCallContext {
|
|||||||
self?.audioLevelPromise.set(.single(level))
|
self?.audioLevelPromise.set(.single(level))
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.audioSessionActiveDisposable.set((audioSessionActive
|
if audioDevice == nil {
|
||||||
|> deliverOn(queue)).start(next: { isActive in
|
strongSelf.audioSessionActiveDisposable.set((audioSessionActive
|
||||||
guard let strongSelf = self else {
|
|> deliverOn(queue)).start(next: { isActive in
|
||||||
return
|
guard let strongSelf = self else {
|
||||||
}
|
return
|
||||||
strongSelf.withContext { context in
|
}
|
||||||
context.nativeSetIsAudioSessionActive(isActive: isActive)
|
strongSelf.withContext { context in
|
||||||
}
|
context.nativeSetIsAudioSessionActive(isActive: isActive)
|
||||||
}))
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.networkTypeDisposable = (updatedNetworkType
|
strongSelf.networkTypeDisposable = (updatedNetworkType
|
||||||
|> deliverOn(queue)).start(next: { networkType in
|
|> deliverOn(queue)).start(next: { networkType in
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
#import "platform/darwin/VideoMetalView.h"
|
#import "platform/darwin/VideoMetalView.h"
|
||||||
#import "platform/darwin/GLVideoView.h"
|
|
||||||
#import "platform/darwin/VideoSampleBufferView.h"
|
#import "platform/darwin/VideoSampleBufferView.h"
|
||||||
#import "platform/darwin/VideoCaptureView.h"
|
#import "platform/darwin/VideoCaptureView.h"
|
||||||
#import "platform/darwin/CustomExternalCapturer.h"
|
#import "platform/darwin/CustomExternalCapturer.h"
|
||||||
|
@ -71,58 +71,537 @@ public:
|
|||||||
virtual ~SharedAudioDeviceModule() = default;
|
virtual ~SharedAudioDeviceModule() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual rtc::scoped_refptr<webrtc::tgcalls_ios_adm::AudioDeviceModuleIOS> audioDeviceModule() = 0;
|
virtual rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> audioDeviceModule() = 0;
|
||||||
|
virtual rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> makeChildAudioDeviceModule() = 0;
|
||||||
virtual void start() = 0;
|
virtual void start() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WrappedAudioDeviceModuleIOS : public tgcalls::DefaultWrappedAudioDeviceModule, public webrtc::AudioTransport {
|
||||||
|
public:
|
||||||
|
WrappedAudioDeviceModuleIOS(webrtc::scoped_refptr<webrtc::AudioDeviceModule> 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<int64_t> 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<WrappedAudioDeviceModuleIOS> 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 {
|
class SharedAudioDeviceModuleImpl: public tgcalls::SharedAudioDeviceModule {
|
||||||
public:
|
public:
|
||||||
SharedAudioDeviceModuleImpl(bool disableAudioInput, bool enableSystemMute) {
|
SharedAudioDeviceModuleImpl(bool disableAudioInput, bool enableSystemMute) {
|
||||||
RTC_DCHECK(tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent());
|
RTC_DCHECK(tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent());
|
||||||
_audioDeviceModule = rtc::make_ref_counted<webrtc::tgcalls_ios_adm::AudioDeviceModuleIOS>(false, disableAudioInput, enableSystemMute, disableAudioInput ? 2 : 1);
|
auto sourceDeviceModule = rtc::make_ref_counted<webrtc::tgcalls_ios_adm::AudioDeviceModuleIOS>(false, disableAudioInput, enableSystemMute, disableAudioInput ? 2 : 1);
|
||||||
|
_audioDeviceModule = rtc::make_ref_counted<WrappedAudioDeviceModuleIOS>(sourceDeviceModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~SharedAudioDeviceModuleImpl() override {
|
virtual ~SharedAudioDeviceModuleImpl() override {
|
||||||
if (tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent()) {
|
if (tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent()) {
|
||||||
if (_audioDeviceModule->Playing()) {
|
_audioDeviceModule->ActualStop();
|
||||||
_audioDeviceModule->StopPlayout();
|
|
||||||
_audioDeviceModule->StopRecording();
|
|
||||||
}
|
|
||||||
_audioDeviceModule = nullptr;
|
_audioDeviceModule = nullptr;
|
||||||
} else {
|
} else {
|
||||||
tgcalls::StaticThreads::getThreads()->getWorkerThread()->BlockingCall([&]() {
|
tgcalls::StaticThreads::getThreads()->getWorkerThread()->BlockingCall([&]() {
|
||||||
if (_audioDeviceModule->Playing()) {
|
_audioDeviceModule->ActualStop();
|
||||||
_audioDeviceModule->StopPlayout();
|
|
||||||
_audioDeviceModule->StopRecording();
|
|
||||||
}
|
|
||||||
_audioDeviceModule = nullptr;
|
_audioDeviceModule = nullptr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual rtc::scoped_refptr<webrtc::tgcalls_ios_adm::AudioDeviceModuleIOS> audioDeviceModule() override {
|
virtual rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> audioDeviceModule() override {
|
||||||
return _audioDeviceModule;
|
return _audioDeviceModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> makeChildAudioDeviceModule() override {
|
||||||
|
return rtc::make_ref_counted<WrappedChildAudioDeviceModule>(_audioDeviceModule);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void start() override {
|
virtual void start() override {
|
||||||
RTC_DCHECK(tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent());
|
RTC_DCHECK(tgcalls::StaticThreads::getThreads()->getWorkerThread()->IsCurrent());
|
||||||
|
|
||||||
_audioDeviceModule->Init();
|
_audioDeviceModule->Start();
|
||||||
if (!_audioDeviceModule->Playing()) {
|
|
||||||
_audioDeviceModule->InitPlayout();
|
|
||||||
//_audioDeviceModule->InitRecording();
|
|
||||||
if (_audioDeviceModule->PlayoutIsInitialized()) {
|
|
||||||
_audioDeviceModule->InternalStartPlayout();
|
|
||||||
}
|
|
||||||
//_audioDeviceModule->InternalStartRecording();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
rtc::scoped_refptr<webrtc::tgcalls_ios_adm::AudioDeviceModuleIOS> _audioDeviceModule;
|
rtc::scoped_refptr<WrappedAudioDeviceModuleIOS> _audioDeviceModule;
|
||||||
};
|
};
|
||||||
|
|
||||||
@implementation SharedCallAudioDevice {
|
@implementation SharedCallAudioDevice {
|
||||||
@ -145,7 +624,8 @@ private:
|
|||||||
|
|
||||||
- (void)setTone:(CallAudioTone * _Nullable)tone {
|
- (void)setTone:(CallAudioTone * _Nullable)tone {
|
||||||
_audioDeviceModule->perform([tone](tgcalls::SharedAudioDeviceModule *audioDeviceModule) {
|
_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;
|
return resultModule;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> {
|
||||||
|
if (audioDeviceModule) {
|
||||||
|
return audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule();
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
},
|
||||||
.directConnectionChannel = directConnectionChannel
|
.directConnectionChannel = directConnectionChannel
|
||||||
});
|
});
|
||||||
_state = OngoingCallStateInitializing;
|
_state = OngoingCallStateInitializing;
|
||||||
@ -2000,6 +2487,13 @@ isConference:(bool)isConference {
|
|||||||
return resultModule;
|
return resultModule;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> {
|
||||||
|
if (audioDeviceModule) {
|
||||||
|
return audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule();
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
},
|
||||||
.onMutedSpeechActivityDetected = [weakSelf, queue](bool value) {
|
.onMutedSpeechActivityDetected = [weakSelf, queue](bool value) {
|
||||||
[queue dispatch:^{
|
[queue dispatch:^{
|
||||||
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 421d23ead1cf8595fb72087a01d7b1452ffd2723
|
Subproject commit b07cb07fb9bcb97f745e74c27f8751e8bef1dfb3
|
Loading…
x
Reference in New Issue
Block a user