mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Conference
This commit is contained in:
parent
15e138e492
commit
5f01a83214
@ -15,216 +15,6 @@ import AccountContext
|
||||
import DeviceProximity
|
||||
import PhoneNumberFormat
|
||||
|
||||
public final class SharedCallAudioContext {
|
||||
let audioDevice: OngoingCallContext.AudioDevice?
|
||||
let callKitIntegration: CallKitIntegration?
|
||||
|
||||
private let defaultToSpeaker: Bool
|
||||
|
||||
private var audioSessionDisposable: Disposable?
|
||||
private var audioSessionShouldBeActiveDisposable: Disposable?
|
||||
private var isAudioSessionActiveDisposable: Disposable?
|
||||
private var audioOutputStateDisposable: Disposable?
|
||||
|
||||
private(set) var audioSessionControl: ManagedAudioSessionControl?
|
||||
|
||||
private let isAudioSessionActivePromise = Promise<Bool>(false)
|
||||
private var isAudioSessionActive: Signal<Bool, NoError> {
|
||||
return self.isAudioSessionActivePromise.get()
|
||||
}
|
||||
|
||||
private let audioOutputStatePromise = Promise<([AudioSessionOutput], AudioSessionOutput?)>(([], nil))
|
||||
private var audioOutputStateValue: ([AudioSessionOutput], AudioSessionOutput?) = ([], nil)
|
||||
public private(set) var currentAudioOutputValue: AudioSessionOutput = .builtin
|
||||
private var didSetCurrentAudioOutputValue: Bool = false
|
||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> {
|
||||
return self.audioOutputStatePromise.get()
|
||||
}
|
||||
|
||||
private let audioSessionShouldBeActive = Promise<Bool>(true)
|
||||
private var initialSetupTimer: Foundation.Timer?
|
||||
|
||||
init(audioSession: ManagedAudioSession, callKitIntegration: CallKitIntegration?, defaultToSpeaker: Bool = false) {
|
||||
self.callKitIntegration = callKitIntegration
|
||||
self.audioDevice = OngoingCallContext.AudioDevice.create(enableSystemMute: false)
|
||||
self.defaultToSpeaker = defaultToSpeaker
|
||||
|
||||
if defaultToSpeaker {
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
self.currentAudioOutputValue = .speaker
|
||||
}
|
||||
|
||||
var didReceiveAudioOutputs = false
|
||||
self.audioSessionDisposable = audioSession.push(audioSessionType: .voiceCall, manualActivate: { [weak self] control in
|
||||
Queue.mainQueue().async {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let previousControl = self.audioSessionControl
|
||||
self.audioSessionControl = control
|
||||
|
||||
if previousControl == nil, let audioSessionControl = self.audioSessionControl {
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
if self.didSetCurrentAudioOutputValue {
|
||||
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||
}
|
||||
} else {
|
||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||
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)
|
||||
|
||||
self.initialSetupTimer?.invalidate()
|
||||
self.initialSetupTimer = Foundation.Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.defaultToSpeaker, let audioSessionControl = self.audioSessionControl {
|
||||
self.currentAudioOutputValue = .speaker
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
if self.didSetCurrentAudioOutputValue {
|
||||
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||
}
|
||||
} else {
|
||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||
audioSessionControl.setup(synchronous: true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, deactivate: { [weak self] _ in
|
||||
return Signal { subscriber in
|
||||
Queue.mainQueue().async {
|
||||
if let self {
|
||||
self.isAudioSessionActivePromise.set(.single(false))
|
||||
self.audioSessionControl = nil
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
return EmptyDisposable
|
||||
}
|
||||
}, availableOutputsChanged: { [weak self] availableOutputs, currentOutput in
|
||||
Queue.mainQueue().async {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.audioOutputStateValue = (availableOutputs, currentOutput)
|
||||
if let currentOutput = currentOutput {
|
||||
self.currentAudioOutputValue = currentOutput
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
}
|
||||
|
||||
var signal: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> = .single((availableOutputs, currentOutput))
|
||||
if !didReceiveAudioOutputs {
|
||||
didReceiveAudioOutputs = true
|
||||
if currentOutput == .speaker {
|
||||
signal = .single((availableOutputs, .builtin))
|
||||
|> then(
|
||||
signal
|
||||
|> delay(1.0, queue: Queue.mainQueue())
|
||||
)
|
||||
}
|
||||
}
|
||||
self.audioOutputStatePromise.set(signal)
|
||||
}
|
||||
})
|
||||
|
||||
self.audioSessionShouldBeActive.set(.single(true))
|
||||
self.audioSessionShouldBeActiveDisposable = (self.audioSessionShouldBeActive.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if value {
|
||||
if let audioSessionControl = self.audioSessionControl {
|
||||
let audioSessionActive: Signal<Bool, NoError>
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
audioSessionActive = callKitIntegration.audioSessionActive
|
||||
} else {
|
||||
audioSessionControl.activate({ _ in })
|
||||
audioSessionActive = .single(true)
|
||||
}
|
||||
self.isAudioSessionActivePromise.set(audioSessionActive)
|
||||
} else {
|
||||
self.isAudioSessionActivePromise.set(.single(false))
|
||||
}
|
||||
} else {
|
||||
self.isAudioSessionActivePromise.set(.single(false))
|
||||
}
|
||||
})
|
||||
|
||||
self.isAudioSessionActiveDisposable = (self.isAudioSessionActive
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.audioDevice?.setIsAudioSessionActive(value)
|
||||
})
|
||||
|
||||
self.audioOutputStateDisposable = (self.audioOutputStatePromise.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.audioOutputStateValue = value
|
||||
if let currentOutput = value.1 {
|
||||
self.currentAudioOutputValue = currentOutput
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.audioSessionDisposable?.dispose()
|
||||
self.audioSessionShouldBeActiveDisposable?.dispose()
|
||||
self.isAudioSessionActiveDisposable?.dispose()
|
||||
self.audioOutputStateDisposable?.dispose()
|
||||
self.initialSetupTimer?.invalidate()
|
||||
}
|
||||
|
||||
func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
self.initialSetupTimer?.invalidate()
|
||||
self.initialSetupTimer = nil
|
||||
|
||||
guard self.currentAudioOutputValue != output else {
|
||||
return
|
||||
}
|
||||
self.currentAudioOutputValue = output
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
|
||||
self.audioOutputStatePromise.set(.single((self.audioOutputStateValue.0, output))
|
||||
|> then(
|
||||
.single(self.audioOutputStateValue)
|
||||
|> delay(1.0, queue: Queue.mainQueue())
|
||||
))
|
||||
|
||||
if let audioSessionControl = self.audioSessionControl {
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||
} else {
|
||||
audioSessionControl.setOutputMode(.custom(output))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func switchToSpeakerIfBuiltin() {
|
||||
if case .builtin = self.currentAudioOutputValue {
|
||||
self.setCurrentAudioOutput(.speaker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class PresentationCallImpl: PresentationCall {
|
||||
public let context: AccountContext
|
||||
private let audioSession: ManagedAudioSession
|
||||
@ -556,7 +346,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
if let data = context.currentAppConfiguration.with({ $0 }).data, let _ = data["ios_killswitch_disable_call_device"] {
|
||||
self.sharedAudioContext = nil
|
||||
} else {
|
||||
self.sharedAudioContext = SharedCallAudioContext(audioSession: audioSession, callKitIntegration: callKitIntegration, defaultToSpeaker: startWithVideo || initialState?.type == .video)
|
||||
self.sharedAudioContext = SharedCallAudioContext.get(audioSession: audioSession, callKitIntegration: callKitIntegration, defaultToSpeaker: startWithVideo || initialState?.type == .video)
|
||||
}
|
||||
|
||||
if let _ = self.sharedAudioContext {
|
||||
|
@ -891,12 +891,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
var sharedAudioContext = sharedAudioContext
|
||||
if sharedAudioContext == nil {
|
||||
var useSharedAudio = !isStream
|
||||
if let data = self.accountContext.currentAppConfiguration.with({ $0 }).data, data["ios_killswitch_group_shared_audio"] != nil {
|
||||
useSharedAudio = false
|
||||
var canReuseCurrent = true
|
||||
if let data = self.accountContext.currentAppConfiguration.with({ $0 }).data {
|
||||
if data["ios_killswitch_group_shared_audio"] != nil {
|
||||
useSharedAudio = false
|
||||
}
|
||||
if data["ios_killswitch_group_shared_audio_reuse"] != nil {
|
||||
canReuseCurrent = false
|
||||
}
|
||||
}
|
||||
|
||||
if useSharedAudio {
|
||||
let sharedAudioContextValue = SharedCallAudioContext(audioSession: audioSession, callKitIntegration: callKitIntegration, defaultToSpeaker: true)
|
||||
let sharedAudioContextValue = SharedCallAudioContext.get(audioSession: audioSession, callKitIntegration: callKitIntegration, defaultToSpeaker: true, reuseCurrent: canReuseCurrent && callKitIntegration == nil)
|
||||
sharedAudioContext = sharedAudioContextValue
|
||||
}
|
||||
}
|
||||
|
231
submodules/TelegramCallsUI/Sources/SharedCallAudioContext.swift
Normal file
231
submodules/TelegramCallsUI/Sources/SharedCallAudioContext.swift
Normal file
@ -0,0 +1,231 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import TelegramVoip
|
||||
import TelegramAudio
|
||||
|
||||
public final class SharedCallAudioContext {
|
||||
private static weak var current: SharedCallAudioContext?
|
||||
|
||||
let audioDevice: OngoingCallContext.AudioDevice?
|
||||
let callKitIntegration: CallKitIntegration?
|
||||
|
||||
private let defaultToSpeaker: Bool
|
||||
|
||||
private var audioSessionDisposable: Disposable?
|
||||
private var audioSessionShouldBeActiveDisposable: Disposable?
|
||||
private var isAudioSessionActiveDisposable: Disposable?
|
||||
private var audioOutputStateDisposable: Disposable?
|
||||
|
||||
private(set) var audioSessionControl: ManagedAudioSessionControl?
|
||||
|
||||
private let isAudioSessionActivePromise = Promise<Bool>(false)
|
||||
private var isAudioSessionActive: Signal<Bool, NoError> {
|
||||
return self.isAudioSessionActivePromise.get()
|
||||
}
|
||||
|
||||
private let audioOutputStatePromise = Promise<([AudioSessionOutput], AudioSessionOutput?)>(([], nil))
|
||||
private var audioOutputStateValue: ([AudioSessionOutput], AudioSessionOutput?) = ([], nil)
|
||||
public private(set) var currentAudioOutputValue: AudioSessionOutput = .builtin
|
||||
private var didSetCurrentAudioOutputValue: Bool = false
|
||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> {
|
||||
return self.audioOutputStatePromise.get()
|
||||
}
|
||||
|
||||
private let audioSessionShouldBeActive = Promise<Bool>(true)
|
||||
private var initialSetupTimer: Foundation.Timer?
|
||||
|
||||
static func get(audioSession: ManagedAudioSession, callKitIntegration: CallKitIntegration?, defaultToSpeaker: Bool = false, reuseCurrent: Bool = false) -> SharedCallAudioContext {
|
||||
if let current = self.current, reuseCurrent {
|
||||
return current
|
||||
}
|
||||
let context = SharedCallAudioContext(audioSession: audioSession, callKitIntegration: callKitIntegration, defaultToSpeaker: defaultToSpeaker)
|
||||
self.current = context
|
||||
return context
|
||||
}
|
||||
|
||||
private init(audioSession: ManagedAudioSession, callKitIntegration: CallKitIntegration?, defaultToSpeaker: Bool = false) {
|
||||
self.callKitIntegration = callKitIntegration
|
||||
self.audioDevice = OngoingCallContext.AudioDevice.create(enableSystemMute: false)
|
||||
|
||||
var defaultToSpeaker = defaultToSpeaker
|
||||
if audioSession.getIsHeadsetPluggedIn() {
|
||||
defaultToSpeaker = false
|
||||
}
|
||||
|
||||
self.defaultToSpeaker = defaultToSpeaker
|
||||
|
||||
if defaultToSpeaker {
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
self.currentAudioOutputValue = .speaker
|
||||
}
|
||||
|
||||
var didReceiveAudioOutputs = false
|
||||
self.audioSessionDisposable = audioSession.push(audioSessionType: .voiceCall, manualActivate: { [weak self] control in
|
||||
Queue.mainQueue().async {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let previousControl = self.audioSessionControl
|
||||
self.audioSessionControl = control
|
||||
|
||||
if previousControl == nil, let audioSessionControl = self.audioSessionControl {
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
if self.didSetCurrentAudioOutputValue {
|
||||
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||
}
|
||||
} else {
|
||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||
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)
|
||||
|
||||
self.initialSetupTimer?.invalidate()
|
||||
self.initialSetupTimer = Foundation.Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.defaultToSpeaker, let audioSessionControl = self.audioSessionControl {
|
||||
self.currentAudioOutputValue = .speaker
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
if self.didSetCurrentAudioOutputValue {
|
||||
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||
}
|
||||
} else {
|
||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||
audioSessionControl.setup(synchronous: true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, deactivate: { [weak self] _ in
|
||||
return Signal { subscriber in
|
||||
Queue.mainQueue().async {
|
||||
if let self {
|
||||
self.isAudioSessionActivePromise.set(.single(false))
|
||||
self.audioSessionControl = nil
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
return EmptyDisposable
|
||||
}
|
||||
}, availableOutputsChanged: { [weak self] availableOutputs, currentOutput in
|
||||
Queue.mainQueue().async {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.audioOutputStateValue = (availableOutputs, currentOutput)
|
||||
if let currentOutput = currentOutput {
|
||||
self.currentAudioOutputValue = currentOutput
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
}
|
||||
|
||||
var signal: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> = .single((availableOutputs, currentOutput))
|
||||
if !didReceiveAudioOutputs {
|
||||
didReceiveAudioOutputs = true
|
||||
if currentOutput == .speaker {
|
||||
signal = .single((availableOutputs, .builtin))
|
||||
|> then(
|
||||
signal
|
||||
|> delay(1.0, queue: Queue.mainQueue())
|
||||
)
|
||||
}
|
||||
}
|
||||
self.audioOutputStatePromise.set(signal)
|
||||
}
|
||||
})
|
||||
|
||||
self.audioSessionShouldBeActive.set(.single(true))
|
||||
self.audioSessionShouldBeActiveDisposable = (self.audioSessionShouldBeActive.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if value {
|
||||
if let audioSessionControl = self.audioSessionControl {
|
||||
let audioSessionActive: Signal<Bool, NoError>
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
audioSessionActive = callKitIntegration.audioSessionActive
|
||||
} else {
|
||||
audioSessionControl.activate({ _ in })
|
||||
audioSessionActive = .single(true)
|
||||
}
|
||||
self.isAudioSessionActivePromise.set(audioSessionActive)
|
||||
} else {
|
||||
self.isAudioSessionActivePromise.set(.single(false))
|
||||
}
|
||||
} else {
|
||||
self.isAudioSessionActivePromise.set(.single(false))
|
||||
}
|
||||
})
|
||||
|
||||
self.isAudioSessionActiveDisposable = (self.isAudioSessionActive
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.audioDevice?.setIsAudioSessionActive(value)
|
||||
})
|
||||
|
||||
self.audioOutputStateDisposable = (self.audioOutputStatePromise.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.audioOutputStateValue = value
|
||||
if let currentOutput = value.1 {
|
||||
self.currentAudioOutputValue = currentOutput
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.audioSessionDisposable?.dispose()
|
||||
self.audioSessionShouldBeActiveDisposable?.dispose()
|
||||
self.isAudioSessionActiveDisposable?.dispose()
|
||||
self.audioOutputStateDisposable?.dispose()
|
||||
self.initialSetupTimer?.invalidate()
|
||||
}
|
||||
|
||||
func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
self.initialSetupTimer?.invalidate()
|
||||
self.initialSetupTimer = nil
|
||||
|
||||
guard self.currentAudioOutputValue != output else {
|
||||
return
|
||||
}
|
||||
self.currentAudioOutputValue = output
|
||||
self.didSetCurrentAudioOutputValue = true
|
||||
|
||||
self.audioOutputStatePromise.set(.single((self.audioOutputStateValue.0, output))
|
||||
|> then(
|
||||
.single(self.audioOutputStateValue)
|
||||
|> delay(1.0, queue: Queue.mainQueue())
|
||||
))
|
||||
|
||||
if let audioSessionControl = self.audioSessionControl {
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||
} else {
|
||||
audioSessionControl.setOutputMode(.custom(output))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func switchToSpeakerIfBuiltin() {
|
||||
if case .builtin = self.currentAudioOutputValue {
|
||||
self.setCurrentAudioOutput(.speaker)
|
||||
}
|
||||
}
|
||||
}
|
@ -262,7 +262,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
||||
case let .chats(chatsNode):
|
||||
count = chatsNode.currentState.selectedPeerIds.count
|
||||
}
|
||||
if isCall && count == 0 {
|
||||
if isCall && count <= 1 {
|
||||
self.titleView.title = CounterControllerTitle(title: self.params.title ?? self.presentationData.strings.Compose_NewGroupTitle, counter: nil)
|
||||
} else {
|
||||
var count = count
|
||||
|
@ -657,6 +657,17 @@ public:
|
||||
|
||||
if (!WrappedInstance()->Playing()) {
|
||||
WrappedInstance()->InitPlayout();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!WrappedInstance()->PlayoutIsInitialized()) {
|
||||
sleep(1);
|
||||
WrappedInstance()->InitPlayout();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!WrappedInstance()->PlayoutIsInitialized()) {
|
||||
return;
|
||||
}
|
||||
WrappedInstance()->StartPlayout();
|
||||
WrappedInstance()->InitRecording();
|
||||
WrappedInstance()->StartRecording();
|
||||
|
Loading…
x
Reference in New Issue
Block a user