[WIP] Conference calls

This commit is contained in:
Isaac 2025-02-11 18:46:59 +04:00
parent 0bb33c215a
commit 1f517e187a
15 changed files with 341 additions and 135 deletions

View File

@ -401,6 +401,22 @@ public extension GroupCallParticipantsContext.Participant {
} }
} }
public struct PresentationGroupCallInvitedPeer: Equatable {
public enum State {
case requesting
case ringing
case connecting
}
public var id: EnginePeer.Id
public var state: State?
public init(id: EnginePeer.Id, state: State?) {
self.id = id
self.state = state
}
}
public protocol PresentationGroupCall: AnyObject { public protocol PresentationGroupCall: AnyObject {
var account: Account { get } var account: Account { get }
var accountContext: AccountContext { get } var accountContext: AccountContext { get }
@ -468,7 +484,7 @@ public protocol PresentationGroupCall: AnyObject {
func invitePeer(_ peerId: EnginePeer.Id) -> Bool func invitePeer(_ peerId: EnginePeer.Id) -> Bool
func removedPeer(_ peerId: EnginePeer.Id) func removedPeer(_ peerId: EnginePeer.Id)
var invitedPeers: Signal<[EnginePeer.Id], NoError> { get } var invitedPeers: Signal<[PresentationGroupCallInvitedPeer], NoError> { get }
var inviteLinks: Signal<GroupCallInviteLinks?, NoError> { get } var inviteLinks: Signal<GroupCallInviteLinks?, NoError> { get }

View File

@ -470,7 +470,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
} }
case .busy: case .busy:
mappedReason = .busy mappedReason = .busy
case .hungUp: case .hungUp, .switchedToConference:
if self.callStartTimestamp != nil { if self.callStartTimestamp != nil {
mappedReason = .hangUp mappedReason = .hangUp
} else { } else {
@ -687,12 +687,12 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
func animateOutToGroupChat(completion: @escaping () -> Void) -> CallController.AnimateOutToGroupChat { func animateOutToGroupChat(completion: @escaping () -> Void) -> CallController.AnimateOutToGroupChat {
self.callScreen.animateOutToGroupChat(completion: completion) self.callScreen.animateOutToGroupChat(completion: completion)
let takenIncomingVideoLayer = self.callScreen.takeIncomingVideoLayer() let takeSource = self.callScreen.takeIncomingVideoLayer()
return CallController.AnimateOutToGroupChat( return CallController.AnimateOutToGroupChat(
containerView: self.containerView, containerView: self.containerView,
incomingPeerId: self.call.peerId, incomingPeerId: (takeSource?.1 ?? true) ? self.call.peerId : self.call.context.account.peerId,
incomingVideoLayer: takenIncomingVideoLayer?.0, incomingVideoLayer: takeSource?.0.0,
incomingVideoPlaceholder: takenIncomingVideoLayer?.1 incomingVideoPlaceholder: takeSource?.0.1
) )
} }

View File

@ -225,7 +225,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
switch type { switch type {
case .busy: case .busy:
statusValue = .text(self.presentationData.strings.Call_StatusBusy) statusValue = .text(self.presentationData.strings.Call_StatusBusy)
case .hungUp, .missed: case .hungUp, .missed, .switchedToConference:
statusValue = .text(self.presentationData.strings.Call_StatusEnded) statusValue = .text(self.presentationData.strings.Call_StatusEnded)
} }
case .error: case .error:

View File

@ -909,7 +909,7 @@ public final class PresentationCallImpl: PresentationCall {
self.conferenceCallImpl = conferenceCall self.conferenceCallImpl = conferenceCall
conferenceCall.upgradedConferenceCall = self conferenceCall.upgradedConferenceCall = self
conferenceCall.setInvitedPeers(self.pendingInviteToConferencePeerIds) conferenceCall.setConferenceInvitedPeers(self.pendingInviteToConferencePeerIds)
for peerId in self.pendingInviteToConferencePeerIds { for peerId in self.pendingInviteToConferencePeerIds {
let _ = conferenceCall.invitePeer(peerId) let _ = conferenceCall.invitePeer(peerId)
} }
@ -990,8 +990,6 @@ public final class PresentationCallImpl: PresentationCall {
return return
} }
self.ongoingContext?.stop(debugLogValue: Promise())
self.ongoingContext = nil
self.ongoingContextStateDisposable?.dispose() self.ongoingContextStateDisposable?.dispose()
self.conferenceStateValue = .ready self.conferenceStateValue = .ready
@ -1197,6 +1195,8 @@ public final class PresentationCallImpl: PresentationCall {
tone = .busy tone = .busy
case .hungUp, .missed: case .hungUp, .missed:
tone = .ended tone = .ended
case .switchedToConference:
tone = nil
} }
case .error: case .error:
tone = .failed tone = .failed

View File

@ -731,7 +731,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
guard let groupCall = conferenceSource.conferenceCall else { guard let groupCall = conferenceSource.conferenceCall else {
return return
} }
(conferenceSource as! PresentationCallImpl).resetAsMovedToConference() (groupCall as! PresentationGroupCallImpl).moveConferenceCall(source: conferenceSource)
self.updateCurrentGroupCall(.group(groupCall)) self.updateCurrentGroupCall(.group(groupCall))
}) })

View File

@ -601,6 +601,10 @@ private final class ScreencastEmbeddedIPCContext: ScreencastIPCContext {
} }
private final class PendingConferenceInvitationContext { private final class PendingConferenceInvitationContext {
enum State {
case ringing
}
private let callSessionManager: CallSessionManager private let callSessionManager: CallSessionManager
private var requestDisposable: Disposable? private var requestDisposable: Disposable?
private var stateDisposable: Disposable? private var stateDisposable: Disposable?
@ -608,7 +612,7 @@ private final class PendingConferenceInvitationContext {
private var didNotifyEnded: Bool = false private var didNotifyEnded: Bool = false
init(callSessionManager: CallSessionManager, groupCall: GroupCallReference, encryptionKey: Data, peerId: PeerId, onEnded: @escaping () -> Void) { init(callSessionManager: CallSessionManager, groupCall: GroupCallReference, encryptionKey: Data, peerId: PeerId, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) {
self.callSessionManager = callSessionManager self.callSessionManager = callSessionManager
self.requestDisposable = (callSessionManager.request(peerId: peerId, isVideo: false, enableVideo: true, conferenceCall: (groupCall, encryptionKey)) self.requestDisposable = (callSessionManager.request(peerId: peerId, isVideo: false, enableVideo: true, conferenceCall: (groupCall, encryptionKey))
@ -624,10 +628,14 @@ private final class PendingConferenceInvitationContext {
return return
} }
switch state.state { switch state.state {
case .dropping, .terminated: case let .requesting(ringing, _):
if ringing {
onStateUpdated(.ringing)
}
case let .dropping(reason), let .terminated(_, reason, _):
if !self.didNotifyEnded { if !self.didNotifyEnded {
self.didNotifyEnded = true self.didNotifyEnded = true
onEnded() onEnded(reason == .ended(.switchedToConference))
} }
default: default:
break break
@ -972,15 +980,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return self.membersPromise.get() return self.membersPromise.get()
} }
private var invitedPeersValue: [EnginePeer.Id] = [] { private var invitedPeersValue: [PresentationGroupCallInvitedPeer] = [] {
didSet { didSet {
if self.invitedPeersValue != oldValue { if self.invitedPeersValue != oldValue {
self.inivitedPeersPromise.set(self.invitedPeersValue) self.inivitedPeersPromise.set(self.invitedPeersValue)
} }
} }
} }
private let inivitedPeersPromise = ValuePromise<[EnginePeer.Id]>([]) private let inivitedPeersPromise = ValuePromise<[PresentationGroupCallInvitedPeer]>([])
public var invitedPeers: Signal<[EnginePeer.Id], NoError> { public var invitedPeers: Signal<[PresentationGroupCallInvitedPeer], NoError> {
return self.inivitedPeersPromise.get() return self.inivitedPeersPromise.get()
} }
@ -1062,14 +1070,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return self.conferenceSourceId return self.conferenceSourceId
} }
var internal_isRemoteConnected = Promise<Bool>()
private var internal_isRemoteConnectedDisposable: Disposable?
public var onMutedSpeechActivityDetected: ((Bool) -> Void)? public var onMutedSpeechActivityDetected: ((Bool) -> Void)?
let debugLog = Promise<String?>() let debugLog = Promise<String?>()
public weak var upgradedConferenceCall: PresentationCallImpl? public weak var upgradedConferenceCall: PresentationCallImpl?
public var pendingDisconnedUpgradedConferenceCall: PresentationCallImpl?
private var pendingDisconnedUpgradedConferenceCallTimer: Foundation.Timer?
private var conferenceInvitationContexts: [PeerId: PendingConferenceInvitationContext] = [:] private var conferenceInvitationContexts: [PeerId: PendingConferenceInvitationContext] = [:]
init( init(
@ -1428,14 +1435,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
self.audioOutputStateDisposable?.dispose() self.audioOutputStateDisposable?.dispose()
self.removedChannelMembersDisposable?.dispose() self.removedChannelMembersDisposable?.dispose()
self.peerUpdatesSubscription?.dispose() self.peerUpdatesSubscription?.dispose()
self.screencastStateDisposable?.dispose() self.screencastStateDisposable?.dispose()
self.pendingDisconnedUpgradedConferenceCallTimer?.invalidate()
self.internal_isRemoteConnectedDisposable?.dispose()
} }
private func switchToTemporaryParticipantsContext(sourceContext: GroupCallParticipantsContext?, oldMyPeerId: PeerId) { private func switchToTemporaryParticipantsContext(sourceContext: GroupCallParticipantsContext?, oldMyPeerId: PeerId) {
@ -1529,7 +1532,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
topParticipants.append(participant) topParticipants.append(participant)
} }
if let index = updatedInvitedPeers.firstIndex(of: participant.peer.id) { if let index = updatedInvitedPeers.firstIndex(where: { $0.id == participant.peer.id }) {
updatedInvitedPeers.remove(at: index) updatedInvitedPeers.remove(at: index)
didUpdateInvitedPeers = true didUpdateInvitedPeers = true
} }
@ -1986,6 +1989,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
lastTimestamp = CFAbsoluteTimeGetCurrent() lastTimestamp = CFAbsoluteTimeGetCurrent()
self.hasActiveIncomingDataValue = true self.hasActiveIncomingDataValue = true
self.activateIncomingAudioIfNeeded()
}) })
self.hasActiveIncomingDataTimer?.invalidate() self.hasActiveIncomingDataTimer?.invalidate()
@ -2002,15 +2007,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}) })
self.signalBarsPromise.set(callContext.signalBars) self.signalBarsPromise.set(callContext.signalBars)
self.internal_isRemoteConnectedDisposable = (self.internal_isRemoteConnected.get()
|> distinctUntilChanged
|> deliverOnMainQueue).startStrict(next: { [weak callContext] isRemoteConnected in
guard let callContext else {
return
}
callContext.addRemoteConnectedEvent(isRemoteConntected: isRemoteConnected)
})
} }
} }
@ -2645,7 +2641,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
} }
if let index = updatedInvitedPeers.firstIndex(of: participant.peer.id) { if let index = updatedInvitedPeers.firstIndex(where: { $0.id == participant.peer.id }) {
updatedInvitedPeers.remove(at: index) updatedInvitedPeers.remove(at: index)
didUpdateInvitedPeers = true didUpdateInvitedPeers = true
} }
@ -2741,6 +2737,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
} }
private func activateIncomingAudioIfNeeded() {
if let genericCallContext = self.genericCallContext, case let .call(groupCall) = genericCallContext {
groupCall.activateIncomingAudio()
}
}
private func requestMediaChannelDescriptions(ssrcs: Set<UInt32>, completion: @escaping ([OngoingGroupCallContext.MediaChannelDescription]) -> Void) -> Disposable { private func requestMediaChannelDescriptions(ssrcs: Set<UInt32>, completion: @escaping ([OngoingGroupCallContext.MediaChannelDescription]) -> Void) -> Disposable {
func extractMediaChannelDescriptions(remainingSsrcs: inout Set<UInt32>, participants: [GroupCallParticipantsContext.Participant], into result: inout [OngoingGroupCallContext.MediaChannelDescription]) { func extractMediaChannelDescriptions(remainingSsrcs: inout Set<UInt32>, participants: [GroupCallParticipantsContext.Participant], into result: inout [OngoingGroupCallContext.MediaChannelDescription]) {
for participant in participants { for participant in participants {
@ -3643,42 +3645,68 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if conferenceInvitationContexts[peerId] != nil { if conferenceInvitationContexts[peerId] != nil {
return false return false
} }
var onEnded: (() -> Void)? var onStateUpdated: ((PendingConferenceInvitationContext.State) -> Void)?
var onEnded: ((Bool) -> Void)?
var didEndAlready = false var didEndAlready = false
let invitationContext = PendingConferenceInvitationContext( let invitationContext = PendingConferenceInvitationContext(
callSessionManager: self.accountContext.account.callSessionManager, callSessionManager: self.accountContext.account.callSessionManager,
groupCall: GroupCallReference(id: initialCall.id, accessHash: initialCall.accessHash), groupCall: GroupCallReference(id: initialCall.id, accessHash: initialCall.accessHash),
encryptionKey: encryptionKey.key, encryptionKey: encryptionKey.key,
peerId: peerId, peerId: peerId,
onEnded: { onStateUpdated: { state in
onStateUpdated?(state)
},
onEnded: { success in
didEndAlready = true didEndAlready = true
onEnded?() onEnded?(success)
} }
) )
if !didEndAlready { if !didEndAlready {
conferenceInvitationContexts[peerId] = invitationContext conferenceInvitationContexts[peerId] = invitationContext
if !self.invitedPeersValue.contains(peerId) { if !self.invitedPeersValue.contains(where: { $0.id == peerId }) {
self.invitedPeersValue.append(peerId) self.invitedPeersValue.append(PresentationGroupCallInvitedPeer(id: peerId, state: .requesting))
} }
onEnded = { [weak self, weak invitationContext] in onStateUpdated = { [weak self] state in
guard let self else {
return
}
if let index = self.invitedPeersValue.firstIndex(where: { $0.id == peerId }) {
var invitedPeer = self.invitedPeersValue[index]
switch state {
case .ringing:
invitedPeer.state = .ringing
}
self.invitedPeersValue[index] = invitedPeer
}
}
onEnded = { [weak self, weak invitationContext] success in
guard let self, let invitationContext else { guard let self, let invitationContext else {
return return
} }
if self.conferenceInvitationContexts[peerId] === invitationContext { if self.conferenceInvitationContexts[peerId] === invitationContext {
self.conferenceInvitationContexts.removeValue(forKey: peerId) self.conferenceInvitationContexts.removeValue(forKey: peerId)
self.invitedPeersValue.removeAll(where: { $0 == peerId })
if success {
if let index = self.invitedPeersValue.firstIndex(where: { $0.id == peerId }) {
var invitedPeer = self.invitedPeersValue[index]
invitedPeer.state = .connecting
self.invitedPeersValue[index] = invitedPeer
}
} else {
self.invitedPeersValue.removeAll(where: { $0.id == peerId })
}
} }
} }
} }
return false return false
} else { } else {
guard let callInfo = self.internalState.callInfo, !self.invitedPeersValue.contains(peerId) else { guard let callInfo = self.internalState.callInfo, !self.invitedPeersValue.contains(where: { $0.id == peerId }) else {
return false return false
} }
var updatedInvitedPeers = self.invitedPeersValue var updatedInvitedPeers = self.invitedPeersValue
updatedInvitedPeers.insert(peerId, at: 0) updatedInvitedPeers.insert(PresentationGroupCallInvitedPeer(id: peerId, state: nil), at: 0)
self.invitedPeersValue = updatedInvitedPeers self.invitedPeersValue = updatedInvitedPeers
let _ = self.accountContext.engine.calls.inviteToGroupCall(callId: callInfo.id, accessHash: callInfo.accessHash, peerId: peerId).start() let _ = self.accountContext.engine.calls.inviteToGroupCall(callId: callInfo.id, accessHash: callInfo.accessHash, peerId: peerId).start()
@ -3687,13 +3715,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
} }
func setInvitedPeers(_ peerIds: [PeerId]) { func setConferenceInvitedPeers(_ peerIds: [PeerId]) {
self.invitedPeersValue = peerIds self.invitedPeersValue = peerIds.map {
PresentationGroupCallInvitedPeer(id: $0, state: .requesting)
}
} }
public func removedPeer(_ peerId: PeerId) { public func removedPeer(_ peerId: PeerId) {
var updatedInvitedPeers = self.invitedPeersValue var updatedInvitedPeers = self.invitedPeersValue
updatedInvitedPeers.removeAll(where: { $0 == peerId}) updatedInvitedPeers.removeAll(where: { $0.id == peerId})
self.invitedPeersValue = updatedInvitedPeers self.invitedPeersValue = updatedInvitedPeers
} }
@ -3859,6 +3889,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
|> runOn(.mainQueue()) |> runOn(.mainQueue())
} }
func moveConferenceCall(source: PresentationCall) {
guard let source = source as? PresentationCallImpl else {
return
}
self.pendingDisconnedUpgradedConferenceCall?.resetAsMovedToConference()
self.pendingDisconnedUpgradedConferenceCall = source
self.pendingDisconnedUpgradedConferenceCallTimer?.invalidate()
self.pendingDisconnedUpgradedConferenceCallTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
if let pendingDisconnedUpgradedConferenceCall = self.pendingDisconnedUpgradedConferenceCall {
self.pendingDisconnedUpgradedConferenceCall = nil
pendingDisconnedUpgradedConferenceCall.resetAsMovedToConference()
}
})
}
} }
private protocol ScreencastContext: AnyObject { private protocol ScreencastContext: AnyObject {

View File

@ -428,6 +428,15 @@ final class VideoChatParticipantVideoComponent: Component {
alphaTransition.setAlpha(view: titleView, alpha: controlsAlpha) alphaTransition.setAlpha(view: titleView, alpha: controlsAlpha)
} }
var previousVideoDescription: GroupCallParticipantsContext.Participant.VideoDescription?
if let previousComponent {
if previousComponent.isMyPeer && previousComponent.isPresentation {
previousVideoDescription = nil
} else {
previousVideoDescription = previousComponent.maxVideoQuality == 0 ? nil : (previousComponent.isPresentation ? previousComponent.participant.presentationDescription : previousComponent.participant.videoDescription)
}
}
let videoDescription: GroupCallParticipantsContext.Participant.VideoDescription? let videoDescription: GroupCallParticipantsContext.Participant.VideoDescription?
if component.isMyPeer && component.isPresentation { if component.isMyPeer && component.isPresentation {
videoDescription = nil videoDescription = nil
@ -506,8 +515,13 @@ final class VideoChatParticipantVideoComponent: Component {
} }
let videoLayer: PrivateCallVideoLayer let videoLayer: PrivateCallVideoLayer
var resetVideoSource = false
if let current = self.videoLayer { if let current = self.videoLayer {
videoLayer = current videoLayer = current
if let previousVideoDescription, previousVideoDescription.endpointId != videoDescription.endpointId {
resetVideoSource = true
}
} else { } else {
videoLayer = PrivateCallVideoLayer() videoLayer = PrivateCallVideoLayer()
self.videoLayer = videoLayer self.videoLayer = videoLayer
@ -517,6 +531,10 @@ final class VideoChatParticipantVideoComponent: Component {
videoLayer.blurredLayer.opacity = 0.0 videoLayer.blurredLayer.opacity = 0.0
resetVideoSource = true
}
if resetVideoSource {
if let input = component.call.video(endpointId: videoDescription.endpointId) { if let input = component.call.video(endpointId: videoDescription.endpointId) {
let videoSource = AdaptedCallVideoSource(videoStreamSignal: input) let videoSource = AdaptedCallVideoSource(videoStreamSignal: input)
self.videoSource = videoSource self.videoSource = videoSource

View File

@ -128,7 +128,7 @@ final class VideoChatParticipantsComponent: Component {
let call: VideoChatCall let call: VideoChatCall
let participants: Participants? let participants: Participants?
let invitedPeers: [EnginePeer] let invitedPeers: [VideoChatScreenComponent.InvitedPeer]
let speakingParticipants: Set<EnginePeer.Id> let speakingParticipants: Set<EnginePeer.Id>
let expandedVideoState: ExpandedVideoState? let expandedVideoState: ExpandedVideoState?
let maxVideoQuality: Int let maxVideoQuality: Int
@ -148,7 +148,7 @@ final class VideoChatParticipantsComponent: Component {
init( init(
call: VideoChatCall, call: VideoChatCall,
participants: Participants?, participants: Participants?,
invitedPeers: [EnginePeer], invitedPeers: [VideoChatScreenComponent.InvitedPeer],
speakingParticipants: Set<EnginePeer.Id>, speakingParticipants: Set<EnginePeer.Id>,
expandedVideoState: ExpandedVideoState?, expandedVideoState: ExpandedVideoState?,
maxVideoQuality: Int, maxVideoQuality: Int,
@ -1259,10 +1259,20 @@ final class VideoChatParticipantsComponent: Component {
) )
} else { } else {
let invitedPeer = component.invitedPeers[i - self.listParticipants.count] let invitedPeer = component.invitedPeers[i - self.listParticipants.count]
participantPeerId = invitedPeer.id participantPeerId = invitedPeer.peer.id
let subtitle: PeerListItemComponent.Subtitle let subtitle: PeerListItemComponent.Subtitle
subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusInvited, color: .neutral) //TODO:localize
switch invitedPeer.state {
case .none:
subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusInvited, color: .neutral)
case .connecting:
subtitle = PeerListItemComponent.Subtitle(text: "connecting...", color: .neutral)
case .requesting:
subtitle = PeerListItemComponent.Subtitle(text: "requesting...", color: .neutral)
case .ringing:
subtitle = PeerListItemComponent.Subtitle(text: "ringing...", color: .neutral)
}
peerItemComponent = PeerListItemComponent( peerItemComponent = PeerListItemComponent(
context: component.call.accountContext, context: component.call.accountContext,
@ -1270,15 +1280,15 @@ final class VideoChatParticipantsComponent: Component {
strings: component.strings, strings: component.strings,
style: .generic, style: .generic,
sideInset: 0.0, sideInset: 0.0,
title: invitedPeer.displayTitle(strings: component.strings, displayOrder: .firstLast), title: invitedPeer.peer.displayTitle(strings: component.strings, displayOrder: .firstLast),
avatarComponent: AnyComponent(VideoChatParticipantAvatarComponent( avatarComponent: AnyComponent(VideoChatParticipantAvatarComponent(
call: component.call, call: component.call,
peer: invitedPeer, peer: invitedPeer.peer,
myPeerId: component.participants?.myPeerId ?? component.call.accountContext.account.peerId, myPeerId: component.participants?.myPeerId ?? component.call.accountContext.account.peerId,
isSpeaking: false, isSpeaking: false,
theme: component.theme theme: component.theme
)), )),
peer: invitedPeer, peer: invitedPeer.peer,
subtitle: subtitle, subtitle: subtitle,
subtitleAccessory: .none, subtitleAccessory: .none,
presence: nil, presence: nil,

View File

@ -191,6 +191,16 @@ final class VideoChatScreenComponent: Component {
} }
} }
struct InvitedPeer: Equatable {
var peer: EnginePeer
var state: PresentationGroupCallInvitedPeer.State?
init(peer: EnginePeer, state: PresentationGroupCallInvitedPeer.State?) {
self.peer = peer
self.state = state
}
}
final class View: UIView, UIGestureRecognizerDelegate { final class View: UIView, UIGestureRecognizerDelegate {
let containerView: UIView let containerView: UIView
@ -242,7 +252,7 @@ final class VideoChatScreenComponent: Component {
var members: PresentationGroupCallMembers? var members: PresentationGroupCallMembers?
var membersDisposable: Disposable? var membersDisposable: Disposable?
var invitedPeers: [EnginePeer] = [] var invitedPeers: [InvitedPeer] = []
var invitedPeersDisposable: Disposable? var invitedPeersDisposable: Disposable?
var speakingParticipantPeers: [EnginePeer] = [] var speakingParticipantPeers: [EnginePeer] = []
@ -323,8 +333,13 @@ final class VideoChatScreenComponent: Component {
} }
var expandedPeer: (id: EnginePeer.Id, isPresentation: Bool)? var expandedPeer: (id: EnginePeer.Id, isPresentation: Bool)?
if let animateOutData, animateOutData.incomingVideoLayer != nil { if let animateOutData, animateOutData.incomingVideoLayer != nil, let members = self.members {
if let members = self.members, let participant = members.participants.first(where: { $0.peer.id == animateOutData.incomingPeerId }) { if let participant = members.participants.first(where: { $0.peer.id == animateOutData.incomingPeerId }) {
if let _ = participant.videoDescription {
expandedPeer = (participant.peer.id, false)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: true)
}
} else if let participant = members.participants.first(where: { $0.peer.id == sourceCallController.call.context.account.peerId }) {
if let _ = participant.videoDescription { if let _ = participant.videoDescription {
expandedPeer = (participant.peer.id, false) expandedPeer = (participant.peer.id, false)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: true) self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: true)
@ -969,7 +984,7 @@ final class VideoChatScreenComponent: Component {
} }
} }
static func groupCallStateForConferenceSource(conferenceSource: PresentationCall) -> Signal<(state: PresentationGroupCallState, invitedPeers: [EnginePeer]), NoError> { static func groupCallStateForConferenceSource(conferenceSource: PresentationCall) -> Signal<(state: PresentationGroupCallState, invitedPeers: [InvitedPeer]), NoError> {
let invitedPeers = conferenceSource.context.engine.data.subscribe( let invitedPeers = conferenceSource.context.engine.data.subscribe(
EngineDataList((conferenceSource as! PresentationCallImpl).pendingInviteToConferencePeerIds.map { TelegramEngine.EngineData.Item.Peer.Peer(id: $0) }) EngineDataList((conferenceSource as! PresentationCallImpl).pendingInviteToConferencePeerIds.map { TelegramEngine.EngineData.Item.Peer.Peer(id: $0) })
) )
@ -982,7 +997,7 @@ final class VideoChatScreenComponent: Component {
conferenceSource.isMuted, conferenceSource.isMuted,
invitedPeers invitedPeers
) )
|> mapToSignal { state, isMuted, invitedPeers -> Signal<(state: PresentationGroupCallState, invitedPeers: [EnginePeer]), NoError> in |> mapToSignal { state, isMuted, invitedPeers -> Signal<(state: PresentationGroupCallState, invitedPeers: [VideoChatScreenComponent.InvitedPeer]), NoError> in
let mappedNetworkState: PresentationGroupCallState.NetworkState let mappedNetworkState: PresentationGroupCallState.NetworkState
switch state.state { switch state.state {
case .active: case .active:
@ -1007,7 +1022,12 @@ final class VideoChatScreenComponent: Component {
isVideoWatchersLimitReached: false isVideoWatchersLimitReached: false
) )
return .single((callState, invitedPeers.compactMap({ $0 }))) return .single((callState, invitedPeers.compactMap({ peer -> VideoChatScreenComponent.InvitedPeer? in
guard let peer else {
return nil
}
return VideoChatScreenComponent.InvitedPeer(peer: peer, state: .requesting)
})))
} }
} }
@ -1023,10 +1043,18 @@ final class VideoChatScreenComponent: Component {
var participants: [GroupCallParticipantsContext.Participant] = [] var participants: [GroupCallParticipantsContext.Participant] = []
let (myPeer, remotePeer) = peers let (myPeer, remotePeer) = peers
if let myPeer { if let myPeer {
var myVideoDescription: GroupCallParticipantsContext.Participant.VideoDescription?
switch state.videoState {
case .active:
myVideoDescription = GroupCallParticipantsContext.Participant.VideoDescription(endpointId: "temp-local", ssrcGroups: [], audioSsrc: nil, isPaused: false)
default:
break
}
participants.append(GroupCallParticipantsContext.Participant( participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer._asPeer(), peer: myPeer._asPeer(),
ssrc: nil, ssrc: nil,
videoDescription: nil, videoDescription: myVideoDescription,
presentationDescription: nil, presentationDescription: nil,
joinTimestamp: 0, joinTimestamp: 0,
raiseHandRating: nil, raiseHandRating: nil,
@ -1040,10 +1068,18 @@ final class VideoChatScreenComponent: Component {
)) ))
} }
if let remotePeer { if let remotePeer {
var remoteVideoDescription: GroupCallParticipantsContext.Participant.VideoDescription?
switch state.remoteVideoState {
case .active:
remoteVideoDescription = GroupCallParticipantsContext.Participant.VideoDescription(endpointId: "temp-remote", ssrcGroups: [], audioSsrc: nil, isPaused: false)
default:
break
}
participants.append(GroupCallParticipantsContext.Participant( participants.append(GroupCallParticipantsContext.Participant(
peer: remotePeer._asPeer(), peer: remotePeer._asPeer(),
ssrc: nil, ssrc: nil,
videoDescription: nil, videoDescription: remoteVideoDescription,
presentationDescription: nil, presentationDescription: nil,
joinTimestamp: 0, joinTimestamp: 0,
raiseHandRating: nil, raiseHandRating: nil,
@ -1087,7 +1123,7 @@ final class VideoChatScreenComponent: Component {
self.members = component.initialData.members self.members = component.initialData.members
self.invitedPeers = component.initialData.invitedPeers self.invitedPeers = component.initialData.invitedPeers
if let members = self.members { if let members = self.members {
self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.id }) }) self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.peer.id }) })
} }
self.callState = component.initialData.callState self.callState = component.initialData.callState
} }
@ -1128,7 +1164,7 @@ final class VideoChatScreenComponent: Component {
self.members = members self.members = members
if let members { if let members {
self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.id }) }) self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.peer.id }) })
} }
if let members, let expandedParticipantsVideoState = self.expandedParticipantsVideoState, !expandedParticipantsVideoState.isUIHidden { if let members, let expandedParticipantsVideoState = self.expandedParticipantsVideoState, !expandedParticipantsVideoState.isUIHidden {
@ -1234,10 +1270,16 @@ final class VideoChatScreenComponent: Component {
self.invitedPeersDisposable = (groupCall.invitedPeers self.invitedPeersDisposable = (groupCall.invitedPeers
|> mapToSignal { invitedPeers in |> mapToSignal { invitedPeers in
return accountContext.engine.data.get( return accountContext.engine.data.get(
EngineDataList(invitedPeers.map({ TelegramEngine.EngineData.Item.Peer.Peer(id: $0) })) EngineDataMap(invitedPeers.map({ TelegramEngine.EngineData.Item.Peer.Peer(id: $0.id) }))
) )
|> map { peers in |> map { peers -> [InvitedPeer] in
return peers.compactMap { $0 } var result: [InvitedPeer] = []
for invitedPeer in invitedPeers {
if let maybePeer = peers[invitedPeer.id], let peer = maybePeer {
result.append(InvitedPeer(peer: peer, state: invitedPeer.state))
}
}
return result
} }
} }
|> deliverOnMainQueue).startStrict(next: { [weak self] invitedPeers in |> deliverOnMainQueue).startStrict(next: { [weak self] invitedPeers in
@ -1247,7 +1289,7 @@ final class VideoChatScreenComponent: Component {
var invitedPeers = invitedPeers var invitedPeers = invitedPeers
if let members { if let members {
invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.id }) }) invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.peer.id }) })
} }
if self.invitedPeers != invitedPeers { if self.invitedPeers != invitedPeers {
@ -2461,13 +2503,13 @@ final class VideoChatScreenV2Impl: ViewControllerComponentContainer, VoiceChatCo
let peer: EnginePeer? let peer: EnginePeer?
let members: PresentationGroupCallMembers? let members: PresentationGroupCallMembers?
let callState: PresentationGroupCallState let callState: PresentationGroupCallState
let invitedPeers: [EnginePeer] let invitedPeers: [VideoChatScreenComponent.InvitedPeer]
init( init(
peer: EnginePeer?, peer: EnginePeer?,
members: PresentationGroupCallMembers?, members: PresentationGroupCallMembers?,
callState: PresentationGroupCallState, callState: PresentationGroupCallState,
invitedPeers: [EnginePeer] invitedPeers: [VideoChatScreenComponent.InvitedPeer]
) { ) {
self.peer = peer self.peer = peer
self.members = members self.members = members
@ -2633,7 +2675,7 @@ final class VideoChatScreenV2Impl: ViewControllerComponentContainer, VoiceChatCo
let accountContext = groupCall.accountContext let accountContext = groupCall.accountContext
let invitedPeers = groupCall.invitedPeers |> take(1) |> mapToSignal { invitedPeers in let invitedPeers = groupCall.invitedPeers |> take(1) |> mapToSignal { invitedPeers in
return accountContext.engine.data.get( return accountContext.engine.data.get(
EngineDataList(invitedPeers.map({ TelegramEngine.EngineData.Item.Peer.Peer(id: $0) })) EngineDataList(invitedPeers.map(\.id).map({ TelegramEngine.EngineData.Item.Peer.Peer(id: $0) }))
) )
} }
return combineLatest( return combineLatest(
@ -2647,7 +2689,12 @@ final class VideoChatScreenV2Impl: ViewControllerComponentContainer, VoiceChatCo
peer: peer, peer: peer,
members: members, members: members,
callState: callState, callState: callState,
invitedPeers: invitedPeers.compactMap { $0 } invitedPeers: invitedPeers.compactMap { peer -> VideoChatScreenComponent.InvitedPeer? in
guard let peer else {
return nil
}
return VideoChatScreenComponent.InvitedPeer(peer: peer, state: nil)
}
) )
} }
case let .conferenceSource(conferenceSource): case let .conferenceSource(conferenceSource):

View File

@ -1891,7 +1891,8 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
self.updateDecorationsColors() self.updateDecorationsColors()
let invitedPeers: Signal<[EnginePeer], NoError> = self.call.invitedPeers let invitedPeers: Signal<[EnginePeer], NoError> = self.call.invitedPeers
|> mapToSignal { ids -> Signal<[EnginePeer], NoError> in |> mapToSignal { peers -> Signal<[EnginePeer], NoError> in
let ids = peers.map(\.id)
return context.engine.data.get(EngineDataList( return context.engine.data.get(EngineDataList(
ids.map(TelegramEngine.EngineData.Item.Peer.Peer.init) ids.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
)) ))

View File

@ -19,6 +19,7 @@ public enum CallSessionEndedType {
case hungUp case hungUp
case busy case busy
case missed case missed
case switchedToConference
} }
public enum CallSessionTerminationReason: Equatable { public enum CallSessionTerminationReason: Equatable {
@ -709,7 +710,7 @@ private final class CallSessionManagerContext {
case .missed: case .missed:
mappedReason = .ended(.missed) mappedReason = .ended(.missed)
case .switchToConference: case .switchToConference:
mappedReason = .ended(.hungUp) mappedReason = .ended(.switchedToConference)
} }
context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason) context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason)
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in |> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
@ -763,7 +764,7 @@ private final class CallSessionManagerContext {
if let (id, accessHash) = dropData { if let (id, accessHash) = dropData {
self.contextIdByStableId.removeValue(forKey: id) self.contextIdByStableId.removeValue(forKey: id)
context.state = .dropping(reason: .ended(.hungUp), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(encryptedGroupKey: encryptedGroupKey)) context.state = .dropping(reason: .ended(.switchedToConference), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(encryptedGroupKey: encryptedGroupKey))
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in |> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
if let strongSelf = self { if let strongSelf = self {
if let context = strongSelf.contexts[internalId] { if let context = strongSelf.contexts[internalId] {
@ -962,11 +963,15 @@ private final class CallSessionManagerContext {
case .phoneCallDiscardReasonDisconnect: case .phoneCallDiscardReasonDisconnect:
parsedReason = .error(.disconnected) parsedReason = .error(.disconnected)
case .phoneCallDiscardReasonHangup: case .phoneCallDiscardReasonHangup:
parsedReason = .ended(.hungUp) if context.pendingConference != nil {
parsedReason = .ended(.switchedToConference)
} else {
parsedReason = .ended(.hungUp)
}
case .phoneCallDiscardReasonMissed: case .phoneCallDiscardReasonMissed:
parsedReason = .ended(.missed) parsedReason = .ended(.missed)
case .phoneCallDiscardReasonAllowGroupCall: case .phoneCallDiscardReasonAllowGroupCall:
parsedReason = .ended(.hungUp) parsedReason = .ended(.switchedToConference)
} }
} else { } else {
parsedReason = .ended(.hungUp) parsedReason = .ended(.hungUp)

View File

@ -503,21 +503,27 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
self.update(transition: .easeInOut(duration: 0.25)) self.update(transition: .easeInOut(duration: 0.25))
} }
public func takeIncomingVideoLayer() -> (CALayer, VideoSource.Output?)? { public func takeIncomingVideoLayer() -> ((CALayer, VideoSource.Output?), Bool)? {
var remoteVideoContainerKey: VideoContainerView.Key? var activeVideoSources: [(VideoContainerView.Key, Bool)] = []
if self.swapLocalAndRemoteVideo { if self.swapLocalAndRemoteVideo {
if let _ = self.activeLocalVideoSource {
activeVideoSources.append((.background, false))
}
if let _ = self.activeRemoteVideoSource { if let _ = self.activeRemoteVideoSource {
remoteVideoContainerKey = .foreground activeVideoSources.append((.foreground, true))
} }
} else { } else {
if let _ = self.activeRemoteVideoSource { if let _ = self.activeRemoteVideoSource {
remoteVideoContainerKey = .background activeVideoSources.append((.background, true))
}
if let _ = self.activeLocalVideoSource {
activeVideoSources.append((.foreground, false))
} }
} }
if let remoteVideoContainerKey, let videoContainerView = self.videoContainerViews.first(where: { $0.key == remoteVideoContainerKey }) { if let videoSource = activeVideoSources.first, let videoContainerView = self.videoContainerViews.first(where: { $0.key == videoSource.0 }) {
videoContainerView.videoContainerLayerTaken = true videoContainerView.videoContainerLayerTaken = true
return (videoContainerView.videoContainerLayer, videoContainerView.currentVideoOutput) return ((videoContainerView.videoContainerLayer, videoContainerView.currentVideoOutput), videoSource.1)
} }
return nil return nil

View File

@ -1087,10 +1087,8 @@ public final class OngoingGroupCallContext {
} }
func addRemoteConnectedEvent(isRemoteConntected: Bool) { func activateIncomingAudio() {
#if os(iOS) self.context.activateIncomingAudio()
self.context.addRemoteConnectedEvent(isRemoteConntected)
#endif
} }
} }
@ -1314,9 +1312,9 @@ public final class OngoingGroupCallContext {
} }
} }
public func addRemoteConnectedEvent(isRemoteConntected: Bool) { public func activateIncomingAudio() {
self.impl.with { impl in self.impl.with { impl in
impl.addRemoteConnectedEvent(isRemoteConntected: isRemoteConntected) impl.activateIncomingAudio()
} }
} }
} }

View File

@ -453,7 +453,7 @@ isConference:(bool)isConference;
- (void)getStats:(void (^ _Nonnull)(OngoingGroupCallStats * _Nonnull))completion; - (void)getStats:(void (^ _Nonnull)(OngoingGroupCallStats * _Nonnull))completion;
- (void)addRemoteConnectedEvent:(bool)isRemoteConnected; - (void)activateIncomingAudio;
@end @end

View File

@ -66,6 +66,29 @@
namespace tgcalls { namespace tgcalls {
class WrappedChildAudioDeviceModuleControl {
public:
WrappedChildAudioDeviceModuleControl() {
}
virtual ~WrappedChildAudioDeviceModuleControl() {
_mutex.Lock();
_mutex.Unlock();
}
public:
void setActive() {
_mutex.Lock();
_mutex.Unlock();
}
private:
webrtc::Mutex _mutex;
};
class SharedAudioDeviceModule { class SharedAudioDeviceModule {
public: public:
virtual ~SharedAudioDeviceModule() = default; virtual ~SharedAudioDeviceModule() = default;
@ -93,12 +116,15 @@ public:
} }
void UpdateAudioCallback(webrtc::AudioTransport *previousAudioCallback, webrtc::AudioTransport *audioCallback) { void UpdateAudioCallback(webrtc::AudioTransport *previousAudioCallback, webrtc::AudioTransport *audioCallback) {
if (audioCallback == nil) { if (audioCallback) {
if (_currentAudioTransport == previousAudioCallback) { _audioTransports.push_back(audioCallback);
_currentAudioTransport = nil; } else if (previousAudioCallback) {
for (size_t i = 0; i < _audioTransports.size(); i++) {
if (_audioTransports[i] == previousAudioCallback) {
_audioTransports.erase(_audioTransports.begin() + i);
break;
}
} }
} else {
_currentAudioTransport = audioCallback;
} }
} }
@ -409,19 +435,26 @@ public:
bool keyPressed, bool keyPressed,
uint32_t& newMicLevel uint32_t& newMicLevel
) override { ) override {
if (_currentAudioTransport) { if (!_audioTransports.empty()) {
return _currentAudioTransport->RecordedDataIsAvailable( for (size_t i = 0; i < _audioTransports.size(); i++) {
audioSamples, auto result = _audioTransports[_audioTransports.size() - 1]->RecordedDataIsAvailable(
nSamples, audioSamples,
nBytesPerSample, nSamples,
nChannels, nBytesPerSample,
samplesPerSec, nChannels,
totalDelayMS, samplesPerSec,
clockDrift, totalDelayMS,
currentMicLevel, clockDrift,
keyPressed, currentMicLevel,
newMicLevel keyPressed,
); newMicLevel
);
if (i == _audioTransports.size() - 1) {
return result;
}
}
return 0;
} else { } else {
return 0; return 0;
} }
@ -440,20 +473,26 @@ public:
uint32_t& newMicLevel, uint32_t& newMicLevel,
absl::optional<int64_t> estimatedCaptureTimeNS absl::optional<int64_t> estimatedCaptureTimeNS
) override { ) override {
if (_currentAudioTransport) { if (!_audioTransports.empty()) {
return _currentAudioTransport->RecordedDataIsAvailable( for (size_t i = 0; i < _audioTransports.size(); i++) {
audioSamples, auto result = _audioTransports[_audioTransports.size() - 1]->RecordedDataIsAvailable(
nSamples, audioSamples,
nBytesPerSample, nSamples,
nChannels, nBytesPerSample,
samplesPerSec, nChannels,
totalDelayMS, samplesPerSec,
clockDrift, totalDelayMS,
currentMicLevel, clockDrift,
keyPressed, currentMicLevel,
newMicLevel, keyPressed,
estimatedCaptureTimeNS newMicLevel,
); estimatedCaptureTimeNS
);
if (i == _audioTransports.size() - 1) {
return result;
}
}
return 0;
} else { } else {
return 0; return 0;
} }
@ -470,8 +509,8 @@ public:
int64_t* elapsed_time_ms, int64_t* elapsed_time_ms,
int64_t* ntp_time_ms int64_t* ntp_time_ms
) override { ) override {
if (_currentAudioTransport) { if (!_audioTransports.empty()) {
return _currentAudioTransport->NeedMorePlayData( return _audioTransports[_audioTransports.size() - 1]->NeedMorePlayData(
nSamples, nSamples,
nBytesPerSample, nBytesPerSample,
nChannels, nChannels,
@ -496,8 +535,8 @@ public:
int64_t* elapsed_time_ms, int64_t* elapsed_time_ms,
int64_t* ntp_time_ms int64_t* ntp_time_ms
) override { ) override {
if (_currentAudioTransport) { if (!_audioTransports.empty()) {
_currentAudioTransport->PullRenderData( _audioTransports[_audioTransports.size() - 1]->PullRenderData(
bits_per_sample, bits_per_sample,
sample_rate, sample_rate,
number_of_channels, number_of_channels,
@ -540,7 +579,7 @@ public:
private: private:
bool _isStarted = false; bool _isStarted = false;
webrtc::AudioTransport *_currentAudioTransport = nullptr; std::vector<webrtc::AudioTransport *> _audioTransports;
}; };
class WrappedChildAudioDeviceModule : public tgcalls::DefaultWrappedAudioDeviceModule { class WrappedChildAudioDeviceModule : public tgcalls::DefaultWrappedAudioDeviceModule {
@ -556,13 +595,28 @@ public:
auto previousAudioCallback = _audioCallback; auto previousAudioCallback = _audioCallback;
_audioCallback = audioCallback; _audioCallback = audioCallback;
((WrappedAudioDeviceModuleIOS *)WrappedInstance().get())->UpdateAudioCallback(previousAudioCallback, audioCallback); if (_isActive) {
((WrappedAudioDeviceModuleIOS *)WrappedInstance().get())->UpdateAudioCallback(previousAudioCallback, audioCallback);
}
return 0; return 0;
} }
public:
void setIsActive() {
if (_isActive) {
return;
}
_isActive = true;
if (_audioCallback) {
((WrappedAudioDeviceModuleIOS *)WrappedInstance().get())->UpdateAudioCallback(nullptr, _audioCallback);
}
}
private: private:
webrtc::AudioTransport *_audioCallback = nullptr; webrtc::AudioTransport *_audioCallback = nullptr;
bool _isActive = false;
}; };
class SharedAudioDeviceModuleImpl: public tgcalls::SharedAudioDeviceModule { class SharedAudioDeviceModuleImpl: public tgcalls::SharedAudioDeviceModule {
@ -1785,7 +1839,9 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}, },
.createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> { .createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> {
if (audioDeviceModule) { if (audioDeviceModule) {
return audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule(); auto result = audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule();
((WrappedChildAudioDeviceModule *)result.get())->setIsActive();
return result;
} else { } else {
return nullptr; return nullptr;
} }
@ -2493,7 +2549,9 @@ isConference:(bool)isConference {
}, },
.createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> { .createWrappedAudioDeviceModule = [audioDeviceModule](webrtc::TaskQueueFactory *taskQueueFactory) -> rtc::scoped_refptr<tgcalls::WrappedAudioDeviceModule> {
if (audioDeviceModule) { if (audioDeviceModule) {
return audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule(); auto result = audioDeviceModule->getSyncAssumingSameThread()->makeChildAudioDeviceModule();
((WrappedChildAudioDeviceModule *)result.get())->setIsActive();
return result;
} else { } else {
return nullptr; return nullptr;
} }
@ -2835,10 +2893,7 @@ isConference:(bool)isConference {
} }
} }
- (void)addRemoteConnectedEvent:(bool)isRemoteConnected { - (void)activateIncomingAudio {
if (_instance) {
_instance->internal_addCustomNetworkEvent(isRemoteConnected);
}
} }
@end @end