Explore multiview video playback in visionOS
Spatial Computing 进阶 20m

visionOS 多画面同时播放视频

Explore multiview video playback in visionOS

2024年6月10日

在 Apple 官方观看视频

一句话判断

visionOS 的 Multiview 让用户同时看最多 5 个视频画面,而且系统帮你搞定了布局、光照效果和音频焦点切换——你只需要提供内容和浏览器 UI。

这场 Session 讲了什么

在 Vision Pro 上看视频已经是顶级体验了,但有些场景需要同时看多个画面:同时关注两场比赛、看几个主播的直播、或者多角度回放精彩片段。visionOS 今年推出的 Multiview 就是为此设计的。

Multiview 的核心思路是:每个视频画面背后是一个独立的 AVPlayerViewController,系统负责把它们排列成舒适的布局。用户可以通过一个 App 自定义的内容浏览器添加视频,点击切换焦点,拖拽调整位置。最多支持 5 个画面,系统会自动选择最优布局模板。

对开发者来说,关键要理解三个类的协作关系:AVPlayerViewController 播放单个视频,AVExperienceController 管理播放体验的切换(内嵌/展开/多画面),AVMultiViewManager 管理所有进入 Multiview 模式的画面。你要做的核心工作是实现一个自定义的浏览器 View Controller,提供内容选择 UI。

值得深挖的点

AVExperienceController:播放体验的状态机

Multiview 不是”打开一个多画面窗口”这么简单,它涉及到单画面和多画面两种体验之间的切换。AVExperienceController 就是管理这个切换的状态机。

每个 AVPlayerViewController 都有一个对应的 AVExperienceController,它定义了该播放器允许切换到哪些体验。默认支持 embedded(内嵌在 App 内容中)和 expanded(全屏展开)。今年新增了 multiview 体验。

当你在 allowedExperiences 里加入 .multiview,播放器的 UI 上就会出现 Multiview 按钮。用户点击后,AVExperienceController 会把播放器从当前体验过渡到 Multiview 模式。这个过渡过程是系统管理的,你不需要手动处理动画和布局。

反过来,当用户在浏览器里移除某个视频,你把对应的 AVExperienceController 切换回 .embedded 或其他体验,AVMultiViewManager 会自动把它从多画面布局中移除。如果这个播放器之前没有被安装到视图层级里,它会直接消失。

内容浏览器:你和系统的协作接口

内容浏览器是 Multiview 中唯一需要你深度定制的部分。它需要做两件事:管理可用的内容列表、提供内容选择的 UI。

把它设置到 AVMultiViewManager 上之后,系统会在用户点击 Multiview 按钮时自动展示浏览器。用户选择了内容后,你需要创建一个新的 AVPlayerViewController,设置好播放内容,把它的 AVExperienceController 切换到 Multiview 模式——然后系统接手后续的布局和渲染。

浏览器还负责响应移除操作。当用户通过浏览器移除一个视频,你把对应播放器的体验切换回非 Multiview 模式。但如果用户直接点击某个画面上的关闭按钮,这个事件不是来自浏览器的——你需要通过 AVExperienceController 的 delegate 来监听所有体验切换事件,统一处理。

代码片段

设置 Multiview 浏览器

// 自定义浏览器 View Controller
class ContentBrowserViewController: UIViewController {
    var availableContent: [VideoItem] = []

    // 用户选择内容后,添加到 Multiview
    func didSelectContent(_ item: VideoItem) {
        // 创建新的播放器
        let playerVC = AVPlayerViewController()
        playerVC.player = AVPlayer(url: item.url)

        // 允许 Multiview 体验
        let experience = playerVC.experienceController
        experience.allowedExperiences.insert(.multiview)

        // 过渡到正在进行的 Multiview 会话
        experience.transition(to: .multiview)
    }
}

// 在 App 启动时设置浏览器
let multiViewManager = AVMultiViewManager()
multiViewManager.browserViewController = ContentBrowserViewController()

场景:直播类 App 让用户同时看多个直播间。坑:新创建的 AVPlayerViewController 此时还没有视图在层级里,不要尝试访问它的 view。

监听体验切换事件

class ExperienceDelegate: NSObject, AVExperienceControllerDelegate {
    func experienceController(
        _ controller: AVExperienceController,
        didUpdateTransition transition: AVExperienceController.Transition,
        context: AVExperienceController.TransitionContext
    ) {
        switch context.state {
        case .started:
            // 用户点击了 Multiview 按钮或关闭按钮
            print("切换开始: \(transition)")
        case .completed:
            // 切换完成,更新 App 状态
            updateAppState(for: transition)
        default:
            break
        }
    }
}

场景:无论是浏览器触发还是用户直接操作画面,都能统一捕获切换事件。坑:didUpdateTransition 会在每次状态变化时调用,包括中间状态,不要在非 completed 状态下做最终的状态更新。

单个播放器启用 Multiview 按钮

let playerVC = AVPlayerViewController()
playerVC.player = AVPlayer(url: streamURL)

// 添加 Multiview 到允许的体验列表
playerVC.experienceController.allowedExperiences = [.embedded, .expanded, .multiview]

// 设置 delegate 监听切换
playerVC.experienceController.delegate = experienceDelegate

场景:为现有播放器加上 Multiview 入口按钮。坑:必须同时支持 .embedded.expanded 作为回退体验,否则用户无法退出 Multiview。

最佳实践

已有项目迁移:如果你的 visionOS App 已经用了 AVPlayerViewController,启用 Multiview 只需要三步:实现浏览器 View Controller、设置到 AVMultiViewManager、在播放器的 allowedExperiences 里加上 .multiview。不需要改动现有的单画面播放逻辑。

新项目起步:在架构视频播放模块时就把 AVExperienceController 作为播放体验的管理入口,而不是只拿 AVPlayerViewController 做播放。这样后续加 Multiview 支持时改动最小。浏览器 UI 的设计建议参考系统相册的多选交互——用户已经形成了这个心智模型。

还有什么值得关注

  • Multiview 布局会自动处理光照效果——多个视频画面的光线会叠加投射到周围环境,比单个画面更沉浸。
  • 音频焦点跟随用户点击的画面自动切换,音量是全局设置的,但播放速度是每个画面独立控制。
  • 最多支持 5 个画面是经过人因工程测试的上限,不要试图绕过这个限制。
WWDC 2024