mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Throttling playback status change
This commit is contained in:
parent
bfa7de8e27
commit
17ef4ae35b
@ -801,7 +801,8 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
strongSelf.updated(transition: .immediate)
|
||||
})
|
||||
|
||||
self.networkStateDisposable = (call.account.networkState |> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
// TODO: retest to uncomment or delete. Relying only on video frames
|
||||
/*self.networkStateDisposable = (call.account.networkState |> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let strongSelf = self else { return }
|
||||
switch state {
|
||||
case .waitingForNetwork, .connecting:
|
||||
@ -828,7 +829,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
if prev != self?.videoStalled {
|
||||
self?.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
})*/
|
||||
|
||||
let callPeer = call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: call.peerId))
|
||||
|
||||
@ -956,6 +957,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
|
||||
public static var body: Body {
|
||||
let background = Child(Rectangle.self)
|
||||
let dismissTapComponent = Child(Rectangle.self)
|
||||
let video = Child(MediaStreamVideoComponent.self)
|
||||
// let navigationBar = Child(NavigationBarComponent.self)
|
||||
// let toolbar = Child(ToolbarComponent.self)
|
||||
@ -1037,6 +1039,14 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
dragOffset = max(context.state.dismissOffset, sheetHeight - context.availableSize.height + context.view.safeAreaInsets.top)// sheetHeight - UIScreen.main.bounds.height
|
||||
}
|
||||
|
||||
let dismissTapAreaHeight = isFullscreen ? 0 : (context.availableSize.height - sheetHeight + dragOffset)
|
||||
let dismissTapComponent = dismissTapComponent.update(
|
||||
component: Rectangle(color: .red.withAlphaComponent(0)),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: dismissTapAreaHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
|
||||
let video = video.update(
|
||||
component: MediaStreamVideoComponent(
|
||||
call: context.component.call,
|
||||
@ -1044,7 +1054,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
isVisible: environment.isVisible && context.state.isVisibleInHierarchy,
|
||||
isAdmin: context.state.canManageCall,
|
||||
peerTitle: context.state.peerTitle,
|
||||
// TODO: find out how to get image
|
||||
// TODO: remove // find out how to get image
|
||||
peerImage: nil,
|
||||
isFullscreen: isFullscreen,
|
||||
videoLoading: context.state.videoStalled,
|
||||
@ -1068,8 +1078,12 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
state?.videoSize = size
|
||||
},
|
||||
onVideoPlaybackLiveChange: { [weak state] isLive in
|
||||
state?.videoStalled = !isLive
|
||||
state?.updated()
|
||||
guard let state else { return }
|
||||
let wasLive = !state.videoStalled
|
||||
if isLive != wasLive {
|
||||
state.videoStalled = !isLive
|
||||
state.updated()
|
||||
}
|
||||
}
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
@ -1381,15 +1395,8 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
let availableSize = context.availableSize
|
||||
let safeAreaTop = context.view.safeAreaInsets.top
|
||||
context.add(background
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
.gesture(.tap { [weak state] in
|
||||
guard let state = state else {
|
||||
return
|
||||
}
|
||||
state.toggleDisplayUI()
|
||||
})
|
||||
.gesture(.pan { [weak state] panState in
|
||||
|
||||
let onPanGesture: ((Gesture.PanGestureState) -> Void) = { [weak state] panState in
|
||||
guard let state = state else {
|
||||
return
|
||||
}
|
||||
@ -1434,11 +1441,31 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.add(background
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
.gesture(.tap { [weak state] in
|
||||
guard let state = state, state.isFullscreen else {
|
||||
return
|
||||
}
|
||||
state.toggleDisplayUI()
|
||||
})
|
||||
.gesture(.pan { panState in
|
||||
onPanGesture(panState)
|
||||
})
|
||||
)
|
||||
// var bottomComponent: AnyComponent<Empty>?
|
||||
// var fullScreenToolbarComponent: AnyComponent<Empty>?
|
||||
|
||||
context.add(dismissTapComponent
|
||||
.position(CGPoint(x: context.availableSize.width / 2, y: dismissTapAreaHeight / 2))
|
||||
.gesture(.tap {
|
||||
_ = call.leave(terminateIfPossible: false)
|
||||
})
|
||||
.gesture(.pan(onPanGesture))
|
||||
)
|
||||
|
||||
if !isFullscreen {
|
||||
let bottomComponent = AnyComponent(ButtonsRowComponent(
|
||||
bottomInset: environment.safeInsets.bottom,
|
||||
|
||||
@ -238,10 +238,10 @@ final class _MediaStreamVideoComponent: Component {
|
||||
// self.loadingBlurView.animator.fractionComplete = intensity
|
||||
// self.loadingBlurView.animator.fractionComplete = 0.4
|
||||
// self.loadingBlurView.effect = UIBlurEffect(style: .light)
|
||||
if let frame = lastFrame[component.call.peerId.id.description] {
|
||||
if let frameView = lastFrame[component.call.peerId.id.description] {
|
||||
placeholderView.subviews.forEach { $0.removeFromSuperview() }
|
||||
placeholderView.addSubview(frame)
|
||||
frame.frame = placeholderView.bounds
|
||||
placeholderView.addSubview(frameView)
|
||||
frameView.frame = placeholderView.bounds
|
||||
// placeholderView.backgroundColor = .green
|
||||
} else {
|
||||
// placeholderView.addSubview(avatarPlaceholderView)
|
||||
@ -252,8 +252,12 @@ final class _MediaStreamVideoComponent: Component {
|
||||
if !hadVideo && placeholderView.superview == nil {
|
||||
addSubview(placeholderView)
|
||||
}
|
||||
|
||||
let needsFadeInAnimation = hadVideo
|
||||
|
||||
if loadingBlurView.superview == nil {
|
||||
addSubview(loadingBlurView)
|
||||
if needsFadeInAnimation {
|
||||
let anim = CABasicAnimation(keyPath: "opacity")
|
||||
anim.duration = 0.5
|
||||
anim.fromValue = 0
|
||||
@ -262,20 +266,22 @@ final class _MediaStreamVideoComponent: Component {
|
||||
anim.isRemovedOnCompletion = false
|
||||
loadingBlurView.layer.add(anim, forKey: "opacity")
|
||||
}
|
||||
}
|
||||
if shimmerBorderLayer.superlayer == nil {
|
||||
// loadingBlurView.contentView.layer.addSublayer(shimmerOverlayLayer)
|
||||
loadingBlurView.contentView.layer.addSublayer(shimmerBorderLayer)
|
||||
}
|
||||
loadingBlurView.clipsToBounds = true
|
||||
if shimmerOverlayLayer.mask == nil {
|
||||
shimmer = .init()
|
||||
shimmer.layer = shimmerOverlayLayer
|
||||
shimmerOverlayView.compositingFilter = "softLightBlendMode"
|
||||
shimmer.testUpdate(background: .clear, foreground: .white.withAlphaComponent(0.4))
|
||||
}
|
||||
// if shimmerOverlayLayer.mask == nil {
|
||||
// shimmer = .init()
|
||||
// shimmer.layer = shimmerOverlayLayer
|
||||
// shimmerOverlayView.compositingFilter = "softLightBlendMode"
|
||||
// shimmer.testUpdate(background: .clear, foreground: .white.withAlphaComponent(0.4))
|
||||
// }
|
||||
// loadingBlurView.layer.cornerRadius = 10
|
||||
shimmerOverlayLayer.opacity = 0.6
|
||||
|
||||
let cornerRadius = loadingBlurView.layer.cornerRadius
|
||||
// shimmerOverlayLayer.opacity = 0.6
|
||||
shimmerBorderLayer.cornerRadius = cornerRadius // TODO: check isFullScreeen
|
||||
shimmerBorderLayer.masksToBounds = true
|
||||
shimmerBorderLayer.compositingFilter = "softLightBlendMode"
|
||||
@ -313,32 +319,32 @@ final class _MediaStreamVideoComponent: Component {
|
||||
anim.toValue = 0
|
||||
anim.fillMode = .forwards
|
||||
anim.isRemovedOnCompletion = false
|
||||
anim.completion = { [self] _ in
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
|
||||
loadingBlurView.removeFromSuperview()
|
||||
// loadingBlurView = .init(effect: UIBlurEffect(style: .light), intensity: 0.4)
|
||||
placeholderView.removeFromSuperview()
|
||||
anim.completion = { [weak self] _ in
|
||||
guard self?.videoStalled == false else { return }
|
||||
self?.loadingBlurView.removeFromSuperview()
|
||||
self?.placeholderView.removeFromSuperview()
|
||||
}
|
||||
loadingBlurView.layer.add(anim, forKey: "opacity")
|
||||
} else {
|
||||
// Accounting for delay in first frame received
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [self] in
|
||||
guard !self.videoStalled else { return }
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
|
||||
guard self?.videoStalled == false else { return }
|
||||
|
||||
// TODO: animate blur intesity with UIPropertyAnimator
|
||||
loadingBlurView.layer.removeAllAnimations()
|
||||
self?.loadingBlurView.layer.removeAllAnimations()
|
||||
let anim = CABasicAnimation(keyPath: "opacity")
|
||||
anim.duration = 0.5
|
||||
anim.fromValue = 1
|
||||
anim.toValue = 0
|
||||
anim.fillMode = .forwards
|
||||
anim.isRemovedOnCompletion = false
|
||||
anim.completion = { _ in
|
||||
anim.completion = { [weak self] _ in
|
||||
guard self?.videoStalled == false else { return }
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
|
||||
self.loadingBlurView.removeFromSuperview()
|
||||
self.placeholderView.removeFromSuperview()
|
||||
self?.loadingBlurView.removeFromSuperview()
|
||||
self?.placeholderView.removeFromSuperview()
|
||||
}
|
||||
loadingBlurView.layer.add(anim, forKey: "opacity")
|
||||
self?.loadingBlurView.layer.add(anim, forKey: "opacity")
|
||||
// UIView.transition(with: self, duration: 0.2, animations: {
|
||||
//// self.loadingBlurView.animator.fractionComplete = 0
|
||||
//// self.loadingBlurView.effect = nil
|
||||
@ -361,6 +367,7 @@ final class _MediaStreamVideoComponent: Component {
|
||||
var timeLastFrameReceived: CFAbsoluteTime?
|
||||
|
||||
var isFullscreen: Bool = false
|
||||
let videoLoadingThrottler = Throttler<Bool>(duration: 1, queue: .main)
|
||||
|
||||
func update(component: _MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
|
||||
self.state = state
|
||||
@ -381,7 +388,7 @@ final class _MediaStreamVideoComponent: Component {
|
||||
})
|
||||
}
|
||||
|
||||
if component.videoLoading || self.videoStalled {
|
||||
if !component.hasVideo || component.videoLoading || self.videoStalled {
|
||||
updateVideoStalled(isStalled: true)
|
||||
} else {
|
||||
updateVideoStalled(isStalled: false)
|
||||
@ -396,10 +403,13 @@ final class _MediaStreamVideoComponent: Component {
|
||||
let currentTime = CFAbsoluteTimeGetCurrent()
|
||||
if let lastFrameTime = strongSelf.timeLastFrameReceived,
|
||||
currentTime - lastFrameTime > 0.5 {
|
||||
DispatchQueue.main.async {
|
||||
strongSelf.videoStalled = true
|
||||
strongSelf.onVideoPlaybackChange(false)
|
||||
// DispatchQueue.main.async {
|
||||
strongSelf.videoLoadingThrottler.publish(true, includingLatest: true) { isStalled in
|
||||
strongSelf.videoStalled = isStalled
|
||||
strongSelf.onVideoPlaybackChange(!isStalled)
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
} }
|
||||
// TODO: use mapToThrottled (?)
|
||||
@ -409,7 +419,7 @@ final class _MediaStreamVideoComponent: Component {
|
||||
// strongSelf.stallTimer?.invalidate()
|
||||
// TODO: optimize with throttle
|
||||
strongSelf.timeLastFrameReceived = CFAbsoluteTimeGetCurrent()
|
||||
DispatchQueue.main.async {
|
||||
// DispatchQueue.main.async {
|
||||
// strongSelf.stallTimer = _stallTimer
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
// print(strongSelf.videoStalled)
|
||||
@ -417,9 +427,13 @@ final class _MediaStreamVideoComponent: Component {
|
||||
// strongSelf.stallTimer?.fire()
|
||||
// }
|
||||
// RunLoop.main.add(strongSelf.stallTimer!, forMode: .common)
|
||||
strongSelf.videoStalled = false
|
||||
strongSelf.onVideoPlaybackChange(true)
|
||||
strongSelf.videoLoadingThrottler.publish(false, includingLatest: true) { isStalled in
|
||||
strongSelf.videoStalled = isStalled
|
||||
strongSelf.onVideoPlaybackChange(!isStalled)
|
||||
}
|
||||
// strongSelf.videoStalled = false
|
||||
// strongSelf.onVideoPlaybackChange(true)
|
||||
// }
|
||||
})
|
||||
stallTimer = _stallTimer
|
||||
// RunLoop.main.add(stallTimer!, forMode: .common)
|
||||
@ -743,10 +757,11 @@ final class _MediaStreamVideoComponent: Component {
|
||||
|
||||
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
// Fading to make
|
||||
let presentation = self.videoView!.snapshotView(afterScreenUpdates: false)!
|
||||
if let presentation = self.videoView!.snapshotView(afterScreenUpdates: false) {
|
||||
self.addSubview(presentation)
|
||||
presentation.frame = self.videoView!.frame
|
||||
lastFrame[self.component!.call.peerId.id.description] = presentation
|
||||
|
||||
// let image = UIGraphicsImageRenderer(size: presentation.bounds.size).image { context in
|
||||
// presentation.render(in: context.cgContext)
|
||||
// }
|
||||
@ -759,6 +774,7 @@ final class _MediaStreamVideoComponent: Component {
|
||||
}, completion: { _ in
|
||||
presentation.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||
// presentation.removeFromSuperlayer()
|
||||
// }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user