AVPlayerLayer 收缩动画

Posted

技术标签:

【中文标题】AVPlayerLayer 收缩动画【英文标题】:AVPlayerLayer shrink animation 【发布时间】:2014-07-12 04:17:56 【问题描述】:

我正在尝试在 AVPlayerLayer 上制作“缩小”动画,但一旦我这样做,视频内容就会从屏幕上消失。动画持有 AVPlayerLayer 的 UIView 作品但不是 AVPlayerLayer。制作动画的正确方法是什么?

 CGRect oldBounds = playerLayer.bounds;
 CGRect newBounds = oldBounds;
 newBounds.size = CGSizeZero;
 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
 animation.duration = 5.f;
 animation.beginTime = CACurrentMediaTime();
 animation.fromValue = [NSValue valueWithCGRect:oldBounds];
 animation.toValue = [NSValue valueWithCGRect:newBounds];


[playerLayer addAnimation:animation forKey:@"shrink"];

【问题讨论】:

您找到解决方案了吗?我正在尝试用 AVPlayerLayer 支持的 UIView 做类似的事情。 【参考方案1】:

默认情况下,当图层被视图控制时,ios 会关闭图层的动画。但是,当您为 UIView 上的属性设置动画时,它会短暂打开图层的动画功能,并将动画参数发送到图层以执行,然后关闭图层上的动画功能。即:做动画是控制UIView,而不是图层。

也就是说,我也不知道如何让我的 AVPlayerLayer 通过我的 UIView 进行动画处理。我上面所说的是它应该如何工作,但我现在也无法让它工作。呵呵。

【讨论】:

【参考方案2】:

这不足为奇。 AVPlayerLayer 是一个特殊的层类,因为它不存在于 QuartzCore 框架中或具有 CA 类前缀。它还经过高度优化,可将视频帧渲染到显示器。尝试通过 UIKit 间接或直接通过 Core Animation 将动画添加到 AVPlayerLayer,对我来说会导致奇怪的行为。

可能将任何类型的动画应用于AVPlayerLayer 的唯一方法是创建主机视图的快照或将图层内容直接渲染到图形上下文中。但是,如果后者不起作用,我不会感到惊讶,因为 AVPlayerLayer 可能不包含 Core Animation 能够渲染的后备存储。

编辑:这是针对此问题的 Playground 解决方案,尽管它确实依赖于使用 UIKit,而不是单独使用 Core Animation。我尚未在设备或模拟器中进行测试,但这似乎可行:

import UIKit
import AVFoundation
import PlaygroundSupport

let rootView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 600.0, height: 400.0))
rootView.backgroundColor = .white

let playerLayerView = UIView()
playerLayerView.frame.size = CGSize(width: 480.0, height: 270.0)
playerLayerView.center = CGPoint(x: rootView.bounds.midX, y: rootView.bounds.midY)

guard let videoUrl = Bundle.main.url(
    forResource: "HEVC_3_iPhone",
    withExtension: "m4v")
    else  fatalError() 

let player = AVPlayer(url: videoUrl)
player.play()

let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = playerLayerView.bounds

playerLayerView.layer.addSublayer(playerLayer)

rootView.addSubview(playerLayerView)

UIView.animate(
    withDuration: 1.0,
    delay: 0.0,
    options: [.autoreverse, .repeat],
    animations: 
        playerLayerView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
    ,
    completion: nil
)

PlaygroundPage.current.liveView = rootView

它是这样的:

如果我理解正确的话,AVPlayerLayer 的“问题”——尽管这不是管道必要性的问题——是它位于标准 UIKit 渲染和合成管道之外。与其他特殊层类(如CAMetalLayer)一起,AVPlayerLayer 在不同的进程中渲染,因为 AVFoundation 需要直接绘制访问不受 UIKit 渲染循环限制的屏幕。所以试图将动画直接附加到图层是行不通的。

然而,将该层放在UIView 中,可使Core Animation 将AVPlayerLayer 合成到视图层次结构中,这意味着视图的几何变化将影响视频播放器层的呈现。 AVFoundation 仍然很可能以它想要的方式渲染视频帧,但它为 Core Animation 提供了一个纹理来合成它想要的任何东西。

这可能会导致微妙的播放器计时抖动,因为您将以前独立渲染的视频强制进入 UIKit 的渲染循环。一般来说,同步这些特殊层是很棘手的,因为 Core Animation 不可避免地必须等待各种进程全部完成为它们的层提供渲染位图,以便它可以将所有内容合成到一个屏幕刷新中。

但这都是我的猜测。

【讨论】:

那么...我们如何在播放视频层的情况下为 UIView 设置动画(大小)? @Thyselius 我用一个对我有用的 Playground 示例更新了我的答案。

以上是关于AVPlayerLayer 收缩动画的主要内容,如果未能解决你的问题,请参考以下文章

包装 AVPlayer 行为时处理 AVPlayerLayer

使用 AutoLayout 设置 AVPlayer/AVPlayerLayer 大小?

在 AVPlayerLayer 和 AVPlayerViewController 中同步视频

AVPlayer

c_cpp 令人敬畏的优化AVPlayer,可在UICollectionView / UITableView中平滑滚动AVPlayerLayer(在iOS10 +上测试)

c_cpp 令人敬畏的优化AVPlayer,可在UICollectionView / UITableView中平滑滚动AVPlayerLayer(在iOS10 +上测试)