Video Chat Improvements

This commit is contained in:
Ilya Laktyushin 2021-05-09 01:10:48 +04:00
parent e7fdd24d55
commit 0cfeab165d

View File

@ -874,6 +874,7 @@ public final class VoiceChatController: ViewController {
let revealOptions: [VoiceChatParticipantItem.RevealOption] = [] let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: nil, text: text, expandedText: expandedText, icon: icon, style: peerEntry.style, enabled: true, transparent: transparent, pinned: peerEntry.pinned, selectable: true, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: { return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: nil, text: text, expandedText: expandedText, icon: icon, style: peerEntry.style, enabled: true, transparent: transparent, pinned: peerEntry.pinned, selectable: true, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: {
return nil
if let endpointId = peerEntry.effectiveVideoEndpointId { if let endpointId = peerEntry.effectiveVideoEndpointId {
return interaction.getPeerVideo(endpointId, peerEntry.style != .list) return interaction.getPeerVideo(endpointId, peerEntry.style != .list)
} else { } else {
@ -925,8 +926,8 @@ public final class VoiceChatController: ViewController {
private let dimNode: ASDisplayNode private let dimNode: ASDisplayNode
private let contentContainer: ASDisplayNode private let contentContainer: ASDisplayNode
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode
private let mainVideoClippingNode: ASDisplayNode private let mainStageVideoClippingNode: ASDisplayNode
private var mainVideoContainerNode: MainVideoContainerNode? private var mainStageVideoContainerNode: MainVideoContainerNode?
private var mainParticipantNode: VoiceChatParticipantItemNode private var mainParticipantNode: VoiceChatParticipantItemNode
private var toggleFullscreenButton: HighlightTrackingButtonNode private var toggleFullscreenButton: HighlightTrackingButtonNode
private let listNode: ListView private let listNode: ListView
@ -1050,6 +1051,7 @@ public final class VoiceChatController: ViewController {
private var currentDominantSpeakerWithVideo: PeerId? private var currentDominantSpeakerWithVideo: PeerId?
private var currentForcedSpeakerWithVideo: PeerId? private var currentForcedSpeakerWithVideo: PeerId?
private var effectiveSpeakerWithVideo: (PeerId, String)? private var effectiveSpeakerWithVideo: (PeerId, String)?
private let updateSpeakerWithVideoDisposable = MetaDisposable()
private var updateAvatarDisposable = MetaDisposable() private var updateAvatarDisposable = MetaDisposable()
private let updateAvatarPromise = Promise<(TelegramMediaImageRepresentation, Float)?>(nil) private let updateAvatarPromise = Promise<(TelegramMediaImageRepresentation, Float)?>(nil)
@ -1089,10 +1091,10 @@ public final class VoiceChatController: ViewController {
self.backgroundNode.backgroundColor = self.isScheduling ? panelBackgroundColor : secondaryPanelBackgroundColor self.backgroundNode.backgroundColor = self.isScheduling ? panelBackgroundColor : secondaryPanelBackgroundColor
self.backgroundNode.clipsToBounds = false self.backgroundNode.clipsToBounds = false
self.mainVideoClippingNode = ASDisplayNode() self.mainStageVideoClippingNode = ASDisplayNode()
self.mainVideoClippingNode.clipsToBounds = true self.mainStageVideoClippingNode.clipsToBounds = true
self.mainVideoContainerNode = MainVideoContainerNode(context: call.accountContext, call: call) self.mainStageVideoContainerNode = MainVideoContainerNode(context: call.accountContext, call: call)
self.mainParticipantNode = VoiceChatParticipantItemNode() self.mainParticipantNode = VoiceChatParticipantItemNode()
@ -1253,13 +1255,13 @@ public final class VoiceChatController: ViewController {
} else { } else {
strongSelf.currentForcedSpeakerWithVideo = nil strongSelf.currentForcedSpeakerWithVideo = nil
} }
strongSelf.updateMainParticipant(waitForFullSize: false) strongSelf.updateMainStageVideo(waitForFullSize: false)
} }
}, togglePeerVideo: { [weak self] peerId in }, togglePeerVideo: { [weak self] peerId in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.mainVideoContainerNode?.otherVideoTapped?() strongSelf.mainStageVideoContainerNode?.otherVideoTapped?()
}, openInvite: { [weak self] in }, openInvite: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -1865,7 +1867,7 @@ public final class VoiceChatController: ViewController {
self.topPanelNode.addSubnode(self.topCornersNode) self.topPanelNode.addSubnode(self.topCornersNode)
self.bottomPanelNode.addSubnode(self.audioButton) self.bottomPanelNode.addSubnode(self.audioButton)
if let _ = self.mainVideoContainerNode { if let _ = self.mainStageVideoContainerNode {
self.bottomPanelNode.addSubnode(self.cameraButton) self.bottomPanelNode.addSubnode(self.cameraButton)
self.bottomPanelNode.addSubnode(self.switchCameraButton) self.bottomPanelNode.addSubnode(self.switchCameraButton)
} }
@ -1877,11 +1879,11 @@ public final class VoiceChatController: ViewController {
self.addSubnode(self.contentContainer) self.addSubnode(self.contentContainer)
self.contentContainer.addSubnode(self.backgroundNode) self.contentContainer.addSubnode(self.backgroundNode)
if let mainVideoContainer = self.mainVideoContainerNode { if let mainVideoContainer = self.mainStageVideoContainerNode {
self.contentContainer.addSubnode(self.mainVideoClippingNode) self.contentContainer.addSubnode(self.mainStageVideoClippingNode)
self.mainVideoClippingNode.addSubnode(mainVideoContainer) self.mainStageVideoClippingNode.addSubnode(mainVideoContainer)
self.mainVideoClippingNode.addSubnode(self.mainParticipantNode) self.mainStageVideoClippingNode.addSubnode(self.mainParticipantNode)
self.mainVideoClippingNode.addSubnode(self.toggleFullscreenButton) self.mainStageVideoClippingNode.addSubnode(self.toggleFullscreenButton)
} }
self.contentContainer.addSubnode(self.listNode) self.contentContainer.addSubnode(self.listNode)
self.contentContainer.addSubnode(self.topPanelNode) self.contentContainer.addSubnode(self.topPanelNode)
@ -2064,9 +2066,7 @@ public final class VoiceChatController: ViewController {
if let (peerId, _) = maxLevelWithVideo { if let (peerId, _) = maxLevelWithVideo {
strongSelf.currentDominantSpeakerWithVideo = peerId strongSelf.currentDominantSpeakerWithVideo = peerId
if !strongSelf.requestedVideoSources.isEmpty { strongSelf.updateMainStageVideo(waitForFullSize: false)
strongSelf.updateMainParticipant(waitForFullSize: false)
}
} }
strongSelf.itemInteraction?.updateAudioLevels(levels) strongSelf.itemInteraction?.updateAudioLevels(levels)
@ -2089,11 +2089,9 @@ public final class VoiceChatController: ViewController {
self.audioButton.addTarget(self, action: #selector(self.audioOutputPressed), forControlEvents: .touchUpInside) self.audioButton.addTarget(self, action: #selector(self.audioOutputPressed), forControlEvents: .touchUpInside)
self.cameraButton.addTarget(self, action: #selector(self.cameraPressed), forControlEvents: .touchUpInside) self.cameraButton.addTarget(self, action: #selector(self.cameraPressed), forControlEvents: .touchUpInside)
self.switchCameraButton.addTarget(self, action: #selector(self.switchCameraPressed), forControlEvents: .touchUpInside) self.switchCameraButton.addTarget(self, action: #selector(self.switchCameraPressed), forControlEvents: .touchUpInside)
self.optionsButton.contextAction = { [weak self] sourceNode, gesture in self.optionsButton.contextAction = { [weak self] sourceNode, gesture in
self?.openContextMenu(sourceNode: sourceNode, gesture: gesture) self?.openContextMenu(sourceNode: sourceNode, gesture: gesture)
} }
self.optionsButton.addTarget(self, action: #selector(self.optionsPressed), forControlEvents: .touchUpInside) self.optionsButton.addTarget(self, action: #selector(self.optionsPressed), forControlEvents: .touchUpInside)
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside) self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside)
@ -2228,12 +2226,7 @@ public final class VoiceChatController: ViewController {
if peerId == strongSelf.currentDominantSpeakerWithVideo { if peerId == strongSelf.currentDominantSpeakerWithVideo {
strongSelf.currentDominantSpeakerWithVideo = nil strongSelf.currentDominantSpeakerWithVideo = nil
} }
strongSelf.updateMainParticipant(waitForFullSize: false) strongSelf.updateMainStageVideo(waitForFullSize: false)
}
} else if strongSelf.currentDominantSpeakerWithVideo != nil && !strongSelf.requestedVideoSources.isEmpty && !strongSelf.didSetMainParticipant {
strongSelf.didSetMainParticipant = true
Queue.mainQueue().after(0.1) {
strongSelf.updateMainParticipant(waitForFullSize: true)
} }
} }
})) }))
@ -2260,7 +2253,7 @@ public final class VoiceChatController: ViewController {
} }
} }
self.mainVideoContainerNode?.tapped = { [weak self] in self.mainStageVideoContainerNode?.tapped = { [weak self] in
if let strongSelf = self, !strongSelf.animatingExpansion { if let strongSelf = self, !strongSelf.animatingExpansion {
var effectiveDisplayMode = strongSelf.displayMode var effectiveDisplayMode = strongSelf.displayMode
var isLandscape = false var isLandscape = false
@ -2398,7 +2391,7 @@ public final class VoiceChatController: ViewController {
} }
} }
} }
self.mainVideoContainerNode?.otherVideoTapped = { [weak self] in self.mainStageVideoContainerNode?.otherVideoTapped = { [weak self] in
if let strongSelf = self, let peerId = strongSelf.effectiveSpeakerWithVideo?.0 { if let strongSelf = self, let peerId = strongSelf.effectiveSpeakerWithVideo?.0 {
let switchingToCamera = !strongSelf.switchedToCameraPeers.contains(peerId) let switchingToCamera = !strongSelf.switchedToCameraPeers.contains(peerId)
if switchingToCamera { if switchingToCamera {
@ -2406,7 +2399,7 @@ public final class VoiceChatController: ViewController {
} else { } else {
strongSelf.switchedToCameraPeers.remove(peerId) strongSelf.switchedToCameraPeers.remove(peerId)
} }
strongSelf.updateMainParticipant(waitForFullSize: false, force: true) strongSelf.updateMainStageVideo(waitForFullSize: false, force: true)
strongSelf.displayToggleVideoSourceTooltip(screencast: !switchingToCamera) strongSelf.displayToggleVideoSourceTooltip(screencast: !switchingToCamera)
} }
} }
@ -2448,6 +2441,7 @@ public final class VoiceChatController: ViewController {
self.voiceSourcesDisposable.dispose() self.voiceSourcesDisposable.dispose()
self.updateAvatarDisposable.dispose() self.updateAvatarDisposable.dispose()
self.ignoreConnectingTimer?.invalidate() self.ignoreConnectingTimer?.invalidate()
self.updateSpeakerWithVideoDisposable.dispose()
} }
@objc private func toggleFullscreenPressed() { @objc private func toggleFullscreenPressed() {
@ -2455,7 +2449,7 @@ public final class VoiceChatController: ViewController {
} else { } else {
self.displayMode = .fullscreen(controlsHidden: true) self.displayMode = .fullscreen(controlsHidden: true)
} }
self.mainVideoContainerNode?.tapped?() self.mainStageVideoContainerNode?.tapped?()
} }
private func openContextMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { private func openContextMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
@ -3471,7 +3465,7 @@ public final class VoiceChatController: ViewController {
} }
private var hasMainVideo: Bool { private var hasMainVideo: Bool {
return self.mainVideoContainerNode != nil && self.effectiveSpeakerWithVideo != nil return self.mainStageVideoContainerNode != nil && self.effectiveSpeakerWithVideo != nil
} }
private var bringVideoToBackOnCompletion = false private var bringVideoToBackOnCompletion = false
@ -3548,7 +3542,7 @@ public final class VoiceChatController: ViewController {
} }
let sideInset: CGFloat = 16.0 let sideInset: CGFloat = 16.0
if let mainVideoContainer = self.mainVideoContainerNode { if let mainVideoContainer = self.mainStageVideoContainerNode {
let videoClippingFrame: CGRect let videoClippingFrame: CGRect
let videoContainerFrame: CGRect let videoContainerFrame: CGRect
let videoInset: CGFloat let videoInset: CGFloat
@ -3615,7 +3609,7 @@ public final class VoiceChatController: ViewController {
transition.updateFrame(node: self.mainParticipantNode, frame: CGRect(x: 0.0, y: videoClippingFrame.height - offset, width: mainParticipantNodeWidth, height: 56.0)) transition.updateFrame(node: self.mainParticipantNode, frame: CGRect(x: 0.0, y: videoClippingFrame.height - offset, width: mainParticipantNodeWidth, height: 56.0))
transition.updateFrame(node: self.toggleFullscreenButton, frame: CGRect(x: mainParticipantNodeWidth + 1.0, y: videoClippingFrame.height - offset + 7.0, width: 44.0, height: 44.0)) transition.updateFrame(node: self.toggleFullscreenButton, frame: CGRect(x: mainParticipantNodeWidth + 1.0, y: videoClippingFrame.height - offset + 7.0, width: 44.0, height: 44.0))
transition.updateFrame(node: self.mainVideoClippingNode, frame: videoClippingFrame) transition.updateFrame(node: self.mainStageVideoClippingNode, frame: videoClippingFrame)
transition.updateFrame(node: mainVideoContainer, frame: videoContainerFrame, completion: { [weak self] _ in transition.updateFrame(node: mainVideoContainer, frame: videoContainerFrame, completion: { [weak self] _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.animatingExpansion = false strongSelf.animatingExpansion = false
@ -3623,7 +3617,7 @@ public final class VoiceChatController: ViewController {
if strongSelf.bringVideoToBackOnCompletion { if strongSelf.bringVideoToBackOnCompletion {
strongSelf.tileListNode.isHidden = true strongSelf.tileListNode.isHidden = true
strongSelf.bringVideoToBackOnCompletion = false strongSelf.bringVideoToBackOnCompletion = false
strongSelf.contentContainer.insertSubnode(strongSelf.mainVideoClippingNode, belowSubnode: strongSelf.tileListNode) strongSelf.contentContainer.insertSubnode(strongSelf.mainStageVideoClippingNode, belowSubnode: strongSelf.tileListNode)
} }
} }
}) })
@ -3966,7 +3960,7 @@ public final class VoiceChatController: ViewController {
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set()) self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
} }
if let videoIndex = self.contentContainer.subnodes?.firstIndex(where: { $0 === self.mainVideoClippingNode }), let listIndex = self.contentContainer.subnodes?.firstIndex(where: { $0 === self.listNode }) { if let videoIndex = self.contentContainer.subnodes?.firstIndex(where: { $0 === self.mainStageVideoClippingNode }), let listIndex = self.contentContainer.subnodes?.firstIndex(where: { $0 === self.listNode }) {
switch effectiveDisplayMode { switch effectiveDisplayMode {
case .default: case .default:
if listIndex < videoIndex { if listIndex < videoIndex {
@ -3974,7 +3968,7 @@ public final class VoiceChatController: ViewController {
} }
case .fullscreen: case .fullscreen:
if listIndex > videoIndex { if listIndex > videoIndex {
self.contentContainer.insertSubnode(self.mainVideoClippingNode, belowSubnode: self.tileListNode) self.contentContainer.insertSubnode(self.mainStageVideoClippingNode, belowSubnode: self.tileListNode)
} }
} }
} }
@ -4117,7 +4111,7 @@ public final class VoiceChatController: ViewController {
let forthButtonFrame: CGRect let forthButtonFrame: CGRect
let leftButtonFrame: CGRect let leftButtonFrame: CGRect
if self.mainVideoContainerNode == nil || isScheduled { if self.mainStageVideoContainerNode == nil || isScheduled {
leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize) leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)
} else { } else {
leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height - upperButtonDistance - cameraButtonSize.height) / 2.0) + upperButtonDistance + cameraButtonSize.height), size: sideButtonSize) leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height - upperButtonDistance - cameraButtonSize.height) / 2.0) + upperButtonDistance + cameraButtonSize.height), size: sideButtonSize)
@ -4138,7 +4132,7 @@ public final class VoiceChatController: ViewController {
if isLandscape { if isLandscape {
let sideInset: CGFloat let sideInset: CGFloat
let buttonsCount: Int let buttonsCount: Int
if self.mainVideoContainerNode == nil { if self.mainStageVideoContainerNode == nil {
sideInset = 42.0 sideInset = 42.0
buttonsCount = 3 buttonsCount = 3
} else { } else {
@ -4711,7 +4705,7 @@ public final class VoiceChatController: ViewController {
} }
if updatePinnedPeer && (previousPinnedPeerEntry?.videoEndpointId != pinnedPeerEntry?.videoEndpointId || previousPinnedPeerEntry?.screencastEndpointId != pinnedPeerEntry?.screencastEndpointId) { if updatePinnedPeer && (previousPinnedPeerEntry?.videoEndpointId != pinnedPeerEntry?.videoEndpointId || previousPinnedPeerEntry?.screencastEndpointId != pinnedPeerEntry?.screencastEndpointId) {
self.updateMainParticipant(waitForFullSize: false, currentEntries: entries, updateMembers: true, force: true) self.updateMainStageVideo(waitForFullSize: false, currentEntries: entries, updateMembers: true, force: true)
return return
} }
@ -4753,8 +4747,7 @@ public final class VoiceChatController: ViewController {
self.enqueueTileTransition(tileTransition) self.enqueueTileTransition(tileTransition)
} }
private var didSetMainParticipant = false private func updateMainStageVideo(waitForFullSize: Bool, currentEntries: [ListEntry]? = nil, updateMembers: Bool = true, force: Bool = false) {
private func updateMainParticipant(waitForFullSize: Bool, currentEntries: [ListEntry]? = nil, updateMembers: Bool = true, force: Bool = false) {
let effectiveMainParticipant = self.currentForcedSpeakerWithVideo ?? self.currentDominantSpeakerWithVideo let effectiveMainParticipant = self.currentForcedSpeakerWithVideo ?? self.currentDominantSpeakerWithVideo
guard effectiveMainParticipant != self.effectiveSpeakerWithVideo?.0 || force else { guard effectiveMainParticipant != self.effectiveSpeakerWithVideo?.0 || force else {
return return
@ -4808,6 +4801,12 @@ public final class VoiceChatController: ViewController {
} }
if effectivePeer == nil { if effectivePeer == nil {
if self.currentForcedSpeakerWithVideo != nil {
self.currentForcedSpeakerWithVideo = nil
}
if self.currentDominantSpeakerWithVideo != nil {
self.currentDominantSpeakerWithVideo = nil
}
effectivePeer = anyPeer effectivePeer = anyPeer
} }
@ -4836,8 +4835,8 @@ public final class VoiceChatController: ViewController {
var waitForFullSize = waitForFullSize var waitForFullSize = waitForFullSize
if !self.isExpanded { if !self.isExpanded {
waitForFullSize = true waitForFullSize = effectivePeer != nil
self.mainVideoClippingNode.alpha = 0.0 self.mainStageVideoClippingNode.alpha = 0.0
} }
self.effectiveSpeakerWithVideo = effectivePeer.flatMap { ($0.0, $0.1) } self.effectiveSpeakerWithVideo = effectivePeer.flatMap { ($0.0, $0.1) }
@ -4845,16 +4844,40 @@ public final class VoiceChatController: ViewController {
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set(), updatePinnedPeer: false) self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set(), updatePinnedPeer: false)
} }
self.call.setFullSizeVideo(endpointId: effectivePeer?.1) self.call.setFullSizeVideo(endpointId: effectivePeer?.1)
self.mainVideoContainerNode?.updatePeer(peer: effectivePeer, waitForFullSize: waitForFullSize, completion: { [weak self] in
self.updateSpeakerWithVideoDisposable.set((self.call.incomingVideoSources
|> mapToSignal { videoSources -> Signal<Bool, NoError> in
if let (_, endpointId, otherEndpointId) = effectivePeer {
var exists = true
if !videoSources.contains(endpointId) {
exists = false
}
if let otherEndpointId = otherEndpointId, !videoSources.contains(otherEndpointId) {
exists = false
}
if exists {
return .single(true)
}
}
return .complete()
}
|> take(1)
|> deliverOnMainQueue
).start(next: { [weak self] _ in
if let strongSelf = self {
strongSelf.mainStageVideoContainerNode?.updatePeer(peer: effectivePeer, waitForFullSize: waitForFullSize, completion: { [weak self] in
if waitForFullSize { if waitForFullSize {
completion() completion()
if let strongSelf = self, strongSelf.mainVideoClippingNode.alpha.isZero { if let strongSelf = self, strongSelf.mainStageVideoClippingNode.alpha.isZero {
strongSelf.mainVideoClippingNode.alpha = 1.0 strongSelf.mainStageVideoClippingNode.alpha = 1.0
strongSelf.mainVideoClippingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) strongSelf.mainStageVideoClippingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
} }
} }
}) })
}
}))
if !waitForFullSize { if !waitForFullSize {
completion() completion()
} }
@ -5399,7 +5422,7 @@ public final class VoiceChatController: ViewController {
private func displayToggleVideoSourceTooltip(screencast: Bool) { private func displayToggleVideoSourceTooltip(screencast: Bool) {
guard let videoContainerNode = self.mainVideoContainerNode, let peerId = self.effectiveSpeakerWithVideo?.0 else { guard let videoContainerNode = self.mainStageVideoContainerNode, let peerId = self.effectiveSpeakerWithVideo?.0 else {
return return
} }