diff --git a/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift b/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift index ed1460c0bf..91cd26170c 100644 --- a/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift @@ -489,8 +489,12 @@ public final class MediaStreamComponent: CombinedComponent { private(set) var displayUI: Bool = true var dismissOffset: CGFloat = 0.0 + var storedIsLandscape: Bool? + let isPictureInPictureSupported: Bool + private var scheduledDismissUITimer: SwiftSignalKit.Timer? + init(call: PresentationGroupCallImpl) { self.call = call @@ -551,6 +555,26 @@ public final class MediaStreamComponent: CombinedComponent { self.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .easeInOut))) } + func cancelScheduledDismissUI() { + self.scheduledDismissUITimer?.invalidate() + self.scheduledDismissUITimer = nil + } + + func scheduleDismissUI() { + if self.scheduledDismissUITimer == nil { + self.scheduledDismissUITimer = SwiftSignalKit.Timer(timeout: 5.0, repeat: false, completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.scheduledDismissUITimer = nil + if strongSelf.displayUI { + strongSelf.toggleDisplayUI() + } + }, queue: .mainQueue()) + self.scheduledDismissUITimer?.start() + } + } + func updateDismissOffset(value: CGFloat, interactive: Bool) { self.dismissOffset = value if interactive { @@ -575,7 +599,8 @@ public final class MediaStreamComponent: CombinedComponent { return { context in let environment = context.environment[ViewControllerComponentContainer.Environment.self].value - if !environment.isVisible { + if environment.isVisible { + } else { context.state.dismissOffset = 0.0 } @@ -675,6 +700,14 @@ public final class MediaStreamComponent: CombinedComponent { ) let isLandscape = context.availableSize.width > context.availableSize.height + if context.state.storedIsLandscape != isLandscape { + context.state.storedIsLandscape = isLandscape + if isLandscape { + context.state.scheduleDismissUI() + } else { + context.state.cancelScheduledDismissUI() + } + } var infoItem: AnyComponent? if let originInfo = context.state.originInfo { @@ -815,6 +848,10 @@ public final class MediaStreamComponentController: ViewControllerComponentContai self.onViewDidAppear?() } + if let view = self.node.hostView.findTaggedView(tag: MediaStreamVideoComponent.View.Tag()) as? MediaStreamVideoComponent.View { + view.expandFromPictureInPicture() + } + self.view.layer.allowsGroupOpacity = true self.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { [weak self] _ in guard let strongSelf = self else { diff --git a/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift b/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift index c67de46857..e6e534a236 100644 --- a/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift @@ -39,7 +39,10 @@ final class MediaStreamVideoComponent: Component { return State() } - public final class View: UIView, AVPictureInPictureControllerDelegate, AVPictureInPictureSampleBufferPlaybackDelegate { + public final class View: UIView, AVPictureInPictureControllerDelegate, AVPictureInPictureSampleBufferPlaybackDelegate, ComponentTaggedView { + public final class Tag { + } + private let videoRenderingContext = VideoRenderingContext() private var videoView: VideoRenderingView? private let blurTintView: UIView @@ -49,6 +52,7 @@ final class MediaStreamVideoComponent: Component { private var pictureInPictureController: AVPictureInPictureController? private var component: MediaStreamVideoComponent? + private var hadVideo: Bool = false override init(frame: CGRect) { self.blurTintView = UIView() @@ -66,6 +70,17 @@ final class MediaStreamVideoComponent: Component { fatalError("init(coder:) has not been implemented") } + public func matches(tag: Any) -> Bool { + if let _ = tag as? Tag { + return true + } + return false + } + + func expandFromPictureInPicture() { + self.pictureInPictureController?.stopPictureInPicture() + } + func update(component: MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize { if component.hasVideo, self.videoView == nil { if let input = component.call.video(endpointId: "unified") { @@ -80,11 +95,12 @@ final class MediaStreamVideoComponent: Component { if #available(iOSApplicationExtension 15.0, iOS 15.0, *), AVPictureInPictureController.isPictureInPictureSupported(), let sampleBufferVideoView = videoView as? SampleBufferVideoRenderingView { let pictureInPictureController = AVPictureInPictureController(contentSource: AVPictureInPictureController.ContentSource(sampleBufferDisplayLayer: sampleBufferVideoView.sampleBufferLayer, playbackDelegate: self)) - self.pictureInPictureController = pictureInPictureController + pictureInPictureController.delegate = self pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = true pictureInPictureController.requiresLinearPlayback = true - pictureInPictureController.delegate = self + + self.pictureInPictureController = pictureInPictureController } videoView.setOnOrientationUpdated { [weak state] _, _ in @@ -95,6 +111,7 @@ final class MediaStreamVideoComponent: Component { return } + strongSelf.hadVideo = true strongSelf.activityIndicatorView?.removeFromSuperview() strongSelf.activityIndicatorView = nil @@ -120,7 +137,9 @@ final class MediaStreamVideoComponent: Component { videoBlurView.updateIsEnabled(true) transition.withAnimation(.none).setFrame(view: videoBlurView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - blurredVideoSize.width) / 2.0), y: floor((availableSize.height - blurredVideoSize.height) / 2.0)), size: blurredVideoSize), completion: nil) } - } else { + } + + if !self.hadVideo { var activityIndicatorTransition = transition let activityIndicatorView: ComponentHostView if let current = self.activityIndicatorView { diff --git a/submodules/TelegramCallsUI/Sources/Components/ViewControllerComponent.swift b/submodules/TelegramCallsUI/Sources/Components/ViewControllerComponent.swift index 18bce06c63..ccc5ae4bee 100644 --- a/submodules/TelegramCallsUI/Sources/Components/ViewControllerComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/ViewControllerComponent.swift @@ -78,12 +78,12 @@ open class ViewControllerComponentContainer: ViewController { } } - private final class Node: ViewControllerTracingNode { + final class Node: ViewControllerTracingNode { private var presentationData: PresentationData private weak var controller: ViewControllerComponentContainer? private let component: AnyComponent - private let hostView: ComponentHostView + let hostView: ComponentHostView private var currentIsVisible: Bool = false private var currentLayout: ContainerViewLayout? @@ -137,7 +137,7 @@ open class ViewControllerComponentContainer: ViewController { } } - private var node: Node { + var node: Node { return self.displayNode as! Node } diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 19da16952e..7790b00796 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -1684,7 +1684,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice) strongSelf.isSpeakingPromise.set(orignalMyLevelHasVoice) - if !missingSsrcs.isEmpty { + if !missingSsrcs.isEmpty && !strongSelf.isStream { strongSelf.participantsContext?.ensureHaveParticipants(ssrcs: missingSsrcs) } })) @@ -2235,6 +2235,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } private func beginTone(tone: PresentationCallTone) { + if self.isStream { + switch tone { + case .groupJoined, .groupLeft: + return + default: + break + } + } var completed: (() -> Void)? let toneRenderer = PresentationCallToneRenderer(tone: tone, completed: { completed?()