Swift/SwiftUI/AVFoundation:有没有办法在新的 AVPlayer 中获取两个 AVPlayer 的 "output" 和 combine/composite?
Swift/SwiftUI/AVFoundation: Is there a way to take the "output" of two AVPlayers and combine/composite that in a new AVPlayer?
假设您有两个(已配置的)AVPlayer
实例(即,它们的 playerItems
加载了一些 AVAsset
,并且呈现出来以便您可以 play/pause,等等)
我想知道是否有任何方法可以获取每个玩家展示的内容(如果愿意,也就是他们的“输出”)并将它们组合成另一个单独的两个“层”AVPlayer
例如,如果您暂停其中一个“源”播放器,该层也会在合成播放器中暂停,或者如果您对其中一个源应用效果,该效果也会反映在合成播放器中 AVPlayer
?
我一直在研究 CADisplayLink
并且想知道这是否是一个选择 - 我应该说我对 AVFoundation 及其相关 API 的经验非常有限,因此我的问题。
提前致谢。
以防万一有人发现这个,下面是我最终解决这个需求的方法:
CADisplayLink
当然与它无关——我认为这个名字暗示了一些事实,但事实并非如此; CADisplayLink
就所有意图和目的而言,是一个与显示刷新率“链接”的计时器 - 非常有用,只是不是我需要的;
- 由于我需要准确显示两个播放器输出的内容,包括应用于媒体的任何处理和过滤器,我还研究了
AVVideoComposition
,并且尽管 可以 用于在有限的媒体(即特定时间长度)内实现类似的效果,但它不适用于我的应用程序,它需要连续显示 - 想想认为它是一种无限长度的媒体,就像直播一样,但它不是直播;
- 经过一些概念和想法的斗争,我意识到我的做法是错误的,而且解决方案其实很简单,想想看。
这是我的解决方案(请记住,我正在开发 SwiftUI 生命周期应用程序,因此有一些选择):
- 我最终创建了一个
UIViewRepresentable
,它创建了一个自定义 UIKit
视图的实例 - 我们称它为 CompositePlayer
;
- 这是一个自定义
UIView
(实际上是一个子 class),它接收两个 AVQueuePlayer
对象作为参数(这些是我加载媒体、应用过滤器的播放器,等等)——让我们称它们为 player_1
和 player_2
(我也将它们声明为 var
,尽管我确信你 不需要 如果你不打算稍后更改对象 - 我做了);
- class 实例化了两个“新的”
AVPlayerLayer
对象(分别为 playerLayer_1
和 playerLayer_2
)
- 然后将这两个接收到的玩家指定为各自的
AVPlayerLayer.player
属性;
- 然后作为
subLayer
以所需的顺序添加到视图 layer
中(即,首先 player_1,然后 player_2,这使得它呈现“on player_1) 的顶部"
伪代码(ish)类似于:
class CompositePlayer: UIView {
var player_1: AVQueuePlayer
var player_2: AVQueuePlayer
private let playerLayer_1: AVPlayerLayer()
private let playerLayer_2: AVPlayerLayer()
// You need to provide any initialiser - it is not mandatory
// to override the default init(frame: CGRect); I'm sure you
// knew that, but n00bs like me don't, really...
init(frame: CGRect, player_1: AVQueuePlayer, player_2: AVQueuePlayer) {
self.player_1 = player_1
self.player_2 = player_2
super.init(frame: frame)
// Now that we are initialised, assign the players to the
// respective player layers...
self.playerLayer_1.player = player_1
self.playerLayer_2.player = player_2
// ... and add these to the view's layer as sublayers in
// the desired order - here, player_2 will render "on top"
// of player_1:
layer.addSublayer(playerLayer_1)
layer.addSubLayer(playerLayer_2)
}
// This is required (as noted by the keyword...) when subclassing
// UIView
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Finally, you need to set the frame size for each layer, which
// is easily achievable by overriding the layoutSubviews() method:
override func layoutSubviews() {
super.layoutSubviews()
playerLayer_1.frame = bounds
playerLayer_2.frame = bounds
}
}
你得到了什么?
你得到一个视图,然后如前所述,通过 UIViewRepresentable
的方式实例化,它能够呈现与普通玩家在其自己的“原始”实例中呈现的完全相同的东西(缺点和所有),因为 AVQueuePlayer
个对象 可以有效地输出到多个视图 ;如果您正确地将正确的对象分配给 UIViewRepresentable
结构的成员(通过使用 EnvironmentObject
),您将获得额外的好处,即能够在其他地方访问两个 playerLayer
对象并且能够设置,例如,该层的不透明度,对其进行转换等
就像变魔术一样,您对“原始”玩家所做的任何操作都将 100% 反映在这个视图上 - 一些 视图固有的显着例外,而不是播放器:例如,设置播放器的 videoGravity
不会影响 此视图的显示,因为您不会对其几何体进行“操作”。
呸。那很长(我真的缩短了它!)。
我希望它对某人有所帮助!
假设您有两个(已配置的)AVPlayer
实例(即,它们的 playerItems
加载了一些 AVAsset
,并且呈现出来以便您可以 play/pause,等等)
我想知道是否有任何方法可以获取每个玩家展示的内容(如果愿意,也就是他们的“输出”)并将它们组合成另一个单独的两个“层”AVPlayer
例如,如果您暂停其中一个“源”播放器,该层也会在合成播放器中暂停,或者如果您对其中一个源应用效果,该效果也会反映在合成播放器中 AVPlayer
?
我一直在研究 CADisplayLink
并且想知道这是否是一个选择 - 我应该说我对 AVFoundation 及其相关 API 的经验非常有限,因此我的问题。
提前致谢。
以防万一有人发现这个,下面是我最终解决这个需求的方法:
CADisplayLink
当然与它无关——我认为这个名字暗示了一些事实,但事实并非如此;CADisplayLink
就所有意图和目的而言,是一个与显示刷新率“链接”的计时器 - 非常有用,只是不是我需要的;- 由于我需要准确显示两个播放器输出的内容,包括应用于媒体的任何处理和过滤器,我还研究了
AVVideoComposition
,并且尽管 可以 用于在有限的媒体(即特定时间长度)内实现类似的效果,但它不适用于我的应用程序,它需要连续显示 - 想想认为它是一种无限长度的媒体,就像直播一样,但它不是直播; - 经过一些概念和想法的斗争,我意识到我的做法是错误的,而且解决方案其实很简单,想想看。
这是我的解决方案(请记住,我正在开发 SwiftUI 生命周期应用程序,因此有一些选择):
- 我最终创建了一个
UIViewRepresentable
,它创建了一个自定义UIKit
视图的实例 - 我们称它为CompositePlayer
; - 这是一个自定义
UIView
(实际上是一个子 class),它接收两个AVQueuePlayer
对象作为参数(这些是我加载媒体、应用过滤器的播放器,等等)——让我们称它们为player_1
和player_2
(我也将它们声明为var
,尽管我确信你 不需要 如果你不打算稍后更改对象 - 我做了); - class 实例化了两个“新的”
AVPlayerLayer
对象(分别为playerLayer_1
和playerLayer_2
) - 然后将这两个接收到的玩家指定为各自的
AVPlayerLayer.player
属性; - 然后作为
subLayer
以所需的顺序添加到视图layer
中(即,首先 player_1,然后 player_2,这使得它呈现“on player_1) 的顶部"
伪代码(ish)类似于:
class CompositePlayer: UIView {
var player_1: AVQueuePlayer
var player_2: AVQueuePlayer
private let playerLayer_1: AVPlayerLayer()
private let playerLayer_2: AVPlayerLayer()
// You need to provide any initialiser - it is not mandatory
// to override the default init(frame: CGRect); I'm sure you
// knew that, but n00bs like me don't, really...
init(frame: CGRect, player_1: AVQueuePlayer, player_2: AVQueuePlayer) {
self.player_1 = player_1
self.player_2 = player_2
super.init(frame: frame)
// Now that we are initialised, assign the players to the
// respective player layers...
self.playerLayer_1.player = player_1
self.playerLayer_2.player = player_2
// ... and add these to the view's layer as sublayers in
// the desired order - here, player_2 will render "on top"
// of player_1:
layer.addSublayer(playerLayer_1)
layer.addSubLayer(playerLayer_2)
}
// This is required (as noted by the keyword...) when subclassing
// UIView
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Finally, you need to set the frame size for each layer, which
// is easily achievable by overriding the layoutSubviews() method:
override func layoutSubviews() {
super.layoutSubviews()
playerLayer_1.frame = bounds
playerLayer_2.frame = bounds
}
}
你得到了什么?
你得到一个视图,然后如前所述,通过 UIViewRepresentable
的方式实例化,它能够呈现与普通玩家在其自己的“原始”实例中呈现的完全相同的东西(缺点和所有),因为 AVQueuePlayer
个对象 可以有效地输出到多个视图 ;如果您正确地将正确的对象分配给 UIViewRepresentable
结构的成员(通过使用 EnvironmentObject
),您将获得额外的好处,即能够在其他地方访问两个 playerLayer
对象并且能够设置,例如,该层的不透明度,对其进行转换等
就像变魔术一样,您对“原始”玩家所做的任何操作都将 100% 反映在这个视图上 - 一些 视图固有的显着例外,而不是播放器:例如,设置播放器的 videoGravity
不会影响 此视图的显示,因为您不会对其几何体进行“操作”。
呸。那很长(我真的缩短了它!)。 我希望它对某人有所帮助!