打造无缝多视角播放体验
Create a seamless multiview playback experience
2025年6月9日
一句话判断
多路音视频同步播放——体育赛事多机位、演唱会多视角、手语流——以前要自己管状态同步和 AirPlay 路由,现在 AVFoundation 和 AVRouting 给了一套完整方案。
这场 Session 讲了什么
Session 聚焦三个痛点:多播放器之间的播放同步、AirPlay 多路流的路由管理、以及多路流的画质优先级控制。
对于需要同步的场景(比如足球赛的不同机位),新增了 AVPlaybackCoordinationMedium,它建立在已有的 AVPlaybackCoordination(SharePlay 用的那个)之上,把多个 player 的状态变更通过消息机制同步。对于不需要同步的场景(比如同时看奥运田径和游泳),也有对应的 API 支持。
AirPlay 方面,AVRoutingPlaybackArbiter 解决了”AirPlay 只能接收一路流”的问题——你可以指定哪个 player 优先路由到大屏,其他继续在本地播放。画质方面,networkResourcePriority 让你标记哪些流更重要,系统在网络带宽有限时优先保证高优先级流的清晰度。
值得深挖的点
AVPlaybackCoordinationMedium 的消息模型
这个 Medium 本质上是一个状态协调中心。每个 AVPlayer 有自己的 AVPlaybackCoordination,Medium 在这些 coordinator 之间传递消息——播放速率、时间跳转、暂停/播放、启动同步、中断恢复。一个 player 暂停,Medium 会通知所有其他 player 同步暂停。实现起来只需要几行代码:创建 Medium,然后把每个 player coordinate 进去。支持 2 个以上的 player。
值得注意的是,这个架构和 SharePlay 的 AVPlaybackCoordination 是同源的。如果你之前做过 Group Activities 的共享播放,迁移到多视角场景几乎是零学习成本。
AVRoutingPlaybackArbiter 的两种路由
Arbiter 管理两类受限路由:不可混合的音频路由(比如 HomePod,同一时间只能播放一个源的音频)和受限的外部视频路由(比如 AirPlay 到 Apple TV,只支持一路视频)。你可以分别设置 preferredParticipantForExternalPlayback(视频优先)和 preferredParticipantForNonMixableAudioRoutes(音频优先)。两个属性可以指向不同的 player。
切换优先级是运行时动态的——用户可以通过 UI 按钮改变哪个 player 是”主视角”,AirPlay 路由会无缝跟着切换,不需要断开重连。
networkResourcePriority 的实际效果
这个优先级不是”保证某个流拿到 N% 带宽”的硬控制,而是给系统带宽分配器的一个信号。系统会综合考虑优先级、播放器视图大小、硬件限制等因素。实际效果是:网络条件好时所有流都是高清,网络变差时低优先级流先降分辨率。Session 的 demo 里,鸟瞰视角(high priority)保持高清,特写视角(low priority)先降了分辨率。
代码片段
1. 多播放器同步播放
场景:体育赛事四个机位同步播放。
import AVFoundation
let closeUpPlayer = AVPlayer(url: closeUpURL)
let birdsEyePlayer = AVPlayer(url: birdsEyeURL)
// 创建协调中心
let coordinationMedium = AVPlaybackCoordinationMedium()
// 把所有 player 连接上去
try coordinationMedium.coordinate(player: closeUpPlayer)
try coordinationMedium.coordinate(player: birdsEyePlayer)
// 现在操作任一 player,其他会自动同步
closeUpPlayer.pause() // 所有 player 同步暂停
closeUpPlayer.seek(to: someTime) // 所有 player 同步 seek
2. AirPlay 多路路由管理
场景:AirPlay 时鸟瞰视角优先上大屏。
import AVRouting
let arbiter = AVRoutingPlaybackArbiter.sharedInstance
arbiter.preferredParticipantForExternalPlayback = birdsEyePlayer
arbiter.preferredParticipantForNonMixableAudioRoutes = birdsEyePlayer
// 用户切换主视角时动态更新
func switchPrimaryView(to player: AVPlayer) {
arbiter.preferredParticipantForExternalPlayback = player
}
3. 画质优先级控制
场景:鸟瞰视角高清优先,观众特写可以降画质。
birdsEyePlayer.networkResourcePriority = .high
crowdCloseUpPlayer.networkResourcePriority = .low
坑:带宽分配受多个因素影响(player 数量、视图大小、硬件),priority 只是信号之一,不能精确控制每个流拿到多少带宽。
最佳实践
如果多个流需要同步(体育、手语、多机位),必须用 AVPlaybackCoordinationMedium。自己实现同步逻辑很容易出问题——seek 时的时间精度、中断恢复、启动时的等待策略,这些 Medium 都帮你处理了。
AirPlay 集成建议从第一天就做。AVRoutingPlaybackArbiter 的 API 很简单,不集成的话用户 AirPlay 时的体验会很混乱(不知道哪路流会上大屏)。
画质优先级方面,根据内容实际重要性来标记,不要全部设 high——全部 high 等于没有优先级。一般主视角 high,辅助视角 low 或不设置(默认)。
还有什么值得关注
- Picture-in-Picture 下的同步:即使切换到 PiP 模式,同步播放依然生效,不需要额外处理。
- Now Playing 界面集成:多路流的播放控制也能在系统 Now Playing 界面中同步操作。
- 非同步多路播放:如果你的场景不需要同步(比如同时看多个比赛),可以直接用多个独立 AVPlayer,配合 Arbiter 管理路由,不需要 Medium。