大多数浏览器和
Developer App 均支持流媒体播放。
-
打造引人入胜的空间照片和视频使用体验
了解如何在 App 中采用空间照片和视频。探索不同类型的立体媒体,并了解如何通过 iPhone 15 Pro 使用你的 iOS App 拍摄空间视频。探索检测和呈现空间媒体的各种方式,包括 visionOS 中全新的 QuickLook Preview Application API。你还将深入了解用于打造空间照片或视频体验的元数据和立体声概念。
章节
- 0:00 - Introduction
- 1:07 - Types of stereoscopic experiences
- 4:13 - Tour of the new APIs
- 13:14 - Deep dive into spatial media formats
资源
- AVCam
- Converting side-by-side 3D video to multiview HEVC and spatial video
- Creating spatial photos and videos with spatial metadata
- Forum: Spatial Computing
- Writing spatial photos
相关视频
WWDC24
WWDC23
-
下载
大家好 我叫 Vedant 是一名 Apple Vision Pro 工程师 在这个讲座中 我将向大家介绍 如何利用空间照片和空间视频 来打造引人入胜的内容 距离我们发布空间照片和视频功能 已经过去一年了 空间照片和视频是一种全新媒体形式 开辟了视觉叙事的 全新维度 广大用户使用这项功能 重温温馨的家庭时光 分享世界各地的动人故事 带我们上天入海 各种精彩内容让我们惊叹不已
而现在 借助机器学习技术 任何照片都可以转换为空间照片 让用户能够以全新方式重温美好回忆
今天 我将介绍 如何在你自己的 App 中 添加空间媒体体验! 首先 我们来看看可用的 立体视频体验类型 以了解空间体验的特别之处 然后 我们介绍 所有全新空间媒体 API 以及它们能够实现的功能 最后 我们将面向 想要创建自定空间媒体的高级用户 对空间媒体格式进行深入探讨 我们首先了解一下 立体视频的不同类型 立体视觉是针对 Vision Pro 增强视频效果的绝佳方式 而 Apple 提供了多种 支持立体视觉的格式 首先是 3D 视频 比如“Apple TV”和“Disney+” App 中提供的 3D 影片 这些视频会渲染到平面屏幕上 但由于内容是立体的 具有景深效果 视频中的动作 可以在屏幕上呈现出 凹凸立体感
然后是可以用 Vision Pro 和 iPhone 15 Pro 拍摄的 空间视频 空间视频通过窗口渲染 窗口边缘周围会微微发光 还会提供自定播放控件 这样可以增强纵深感 并能缓解会导致观看者感到不适的 一些常见问题 作为一种“傻瓜相机”拍摄格式 空间视频的效果很棒 用户可以使用 iPhone 等设备 拍出观看体验舒适 又引人入胜的内容 而无需精通 3D 影片的拍摄规则 还有适用于高端专业内容的 Apple Immersive 视频 这类视频在拍摄时采用 180 度 8K 分辨率 附加空间音频 现在 在 visionOS 2.0 中 这类视频还可以提供预览展示 和空间视频一样 预览演示 通过尺寸较小的浮动窗口进行渲染 这样一来 观看者就可以 观看立体的视频 同时仍然能够与其他 App 交互
所有这些体验看起来都很棒 但在完全沉浸模式下 它们才真正大放异彩 完全沉浸模式能让其他 App 退到背景 让视频内容占据焦点 这几类视频都有各自的方式 能以完全沉浸模式显示 3D 视频会停靠在环境中 而屏幕会向后移动并放大 这是我最喜欢的观影方式 对于 2D 和 3D 影片都是如此
要了解如何为你的 App 添加停靠效果 请观看标题为 “在自定 App 环境中提升 视频播放效果和沉浸感”的视频
空间视频可以展开进入沉浸式展示 根据对象的实际大小 来进行内容的缩放 边框的边缘会消失 内容无缝融入到现实世界中 让人感觉就像置身于内容之中 而不仅仅是在观看内容
空间视频能让观看者完全沉浸其中 只需轻点右上角的 Immersive 按钮即可
还可以通过底部的退出按钮 收起全沉浸式显示
从而回到窗口式展示 这种显示方式支持在共享空间中 使用其他 App 进行多任务处理
对于 Apple Immersive 视频 视频将展开 进入原生体验 视频画面会围绕观看者来显示 并完全停用透视 让观看者置身于操作内 这为专业创意叙事 开辟了全新可能 能够以可实现的最高保真度 带给观看者身临其境的体验
3D 视频、空间视频 和 Apple Immersive 视频 共同构成了一个丰富多彩的 立体内容生态系统 其中每一类视频 所适合的场景各不相同 而 visionOS 提供的 立体媒体体验 除了上述视频之外 还有空间照片 这类媒体使用的传送门 和沉浸式展示 与空间视频相同 开发者还可以使用 API、AVFoundation 和 RealityKit 打造 自定视频体验 此外 立体视觉给照片和视频 带来的提升 也同样适用于实时 3D 内容 也能产生令人惊叹的效果 交互式内容的立体效果尤其惊艳 如需详细了解如何实现 这样的效果 请观看视频 “将 iOS 或 iPadOS 游戏 移植到 visionOS”
现在 我们知道了什么是空间媒体 接下来看看如何在 App 中添加它们 在这一部分中 我们将介绍 API 的三个新领域 空间视频录制、 空间媒体文件的检测和载入 以及在 visionOS 上 内容展示方式的不同选项 通过这一部分 你会注意到 空间媒体没有新的框架 这是因为空间媒体的框架 已经完全整合到大家已经 熟悉并喜爱的现有 Apple 框架中 包括 AVFoundation、PhotoKit、 QuickLook、WebKit 等 只需编写几行代码 就可以在 App 中添加空间媒体 所采用的方法和其他各类媒体相同 首先 我们来了解一下 空间视频的录制方式
在 iPhone 15 Pro 上 我们将广角和超广角摄像头 并排放置 而不是呈对角放置 通过这项变化 来支持录制空间视频 这意味着当用户横向握持手机时 两个摄像头位于同一条水平基准线上 与人眼的方向相同 这样一来 用户使用“相机”App 就能拍摄空间视频 现在 我们将公开这个 API 让任何 App 都能录制空间视频 我们来看看具体操作 我们将编写一些代码 来录制普通的 2D 视频 然后针对空间媒体进行扩展
我们将借助 AVCapture 使用以下数据流 首先需要一个 DeviceInput 来表示来自相机等 CaptureDevice 的输入 然后需要一个 MovieFileOutput 来管理输出并将输出写入磁盘 输入和输出通过 AVCapture Connection 连接 还有一个 AVCaptureSession 负责协调这些组件 之间的数据流
接下来编写相应的代码 首先创建 AVCaptureSession 然后创建 AVCaptureDevice 并暂时将它设为默认的 systemPreferredCamera
然后 在会话中添加一个输入
在会话中添加一个输出
提交配置 然后开始运行 如果你以前使用过 Camera Capture API 那么你应该会觉得 这个流程看起来非常熟悉 如果你还没使用过这个 API 建议访问 developer.apple.com 查看 AVCam 示例代码项目
接下来 我们扩展这段代码 来录制空间视频 我们只需要做三件事 更改 AVCaptureDevice 选择一种支持的视频格式 并在输出上启用空间视频录制 首先更改 AVCaptureDevice 我们将使用 builtInDualWideCamera 因为空间视频需要 广角和超广角摄像头 同时进行流处理
接下来 选择一种 支持空间视频的视频格式 遍历 videoDevice 中的格式 检查 isSpatialVideoCaptureSupported 是否为 true 如果找到这项为 true 的格式 就将它设为 videoDevice 上的 activeFormat 我们需要确保恰当地 锁定和解锁 videoDevice 接下来添加一些错误处理方法 如果没有找到任何 符合条件的空间格式 就会返回 false
最后 我们来检查 是否支持空间视频拍摄 如果支持 就将 isSpatialVideoCaptureEnabled 设为 true 请注意 这种方法仅适用于 iPhone 15 Pro 而且 isSpatialVideoCaptureSupported 在其他设备上会返回 false 在这种情况下 你需要 恰当地处理错误
这样就可以了! 只需完成这三处小改动 就可以开始录制空间视频了! 所有复杂的同步、摄像头校准、 编码和元数据写入 都通过抽象处理得以省略 你可以直接在磁盘上 获得来自 movieFileOutput 的 完整空间视频文件 就是这么简单 而除此之外 我们还可以添加两项功能 来进一步提高录制质量 经过改进的视频防抖功能 和出色的预览功能 让我们来看看该如何实现
为了实现最佳防抖效果 将 preferredVideoStabilizationMode 设为 cinematicExtendedEnhanced 这样就能延长预查窗口期 并在画面中裁剪更多区域 从而让视频变得更加流畅 我们都希望拍出流畅的视频 但在使用 visionOS 时尤其重要 因为设置的虚拟屏幕尺寸 也许远大于其他设备
可以使用 AVCaptureVideoPreviewLayer API 获取预览视频流 这种方法与其他视频格式 的处理方法相同 但是 由于 iPhone 的显示屏 采用了单眼成像技术 因此无法提供空间视频预览 所以无论设备方向如何 预览都会 始终显示广角摄像头拍摄的画面
请务必注意 用于拍摄空间视频的两个摄像头 即广角和超广角摄像头 实际上是不同类型的摄像头! 广角摄像头是 iPhone 上 常规摄影表现最出色的摄像头 而超广角镜头经过优化 能够以非常广阔的视角进行拍摄 也能进行特写对焦 两个摄像头收集光线的能力 以及最小焦距 各不相同 拍摄空间视频时 在大多数情况下 这些差异不太明显 这要归功于我们用来修正空间视频的 计算摄影技术 但是 在弱光条件下拍摄空间视频时 每个摄像头的噪点水平 可能会有所不同 导致用户在 Vision Pro 中 观看时感到不适 同样 如果拍摄主体 与摄像头之间的距离 小于最小焦距 可能会导致失焦 从而让观看者感到不适 由于 iPhone 预览采用单眼成像 因此你可能要等到稍后 在 Vision Pro 上观看视频时 才会注意到这些问题 而到那时 你试图捕捉的那个时刻 可能已经过去了 重新录制为时已晚 为了解决这个问题 我们在 AVCaptureDevice 上 创建了新的 spatialCaptureDiscomfortReasons 变量 你可以通过键值观察 来观察 App 中的这个变量 并在检测到 .subjectTooClose 或 .notEnoughLight 条件时 渲染相应的引导性 UI
看这里 iPhone“相机”App 会在我距离拍摄对象太近时 显示警告信息 并在我向后移动后关闭警告信息 我们建议你在 App 中 显示一些类似的引导性 UI 现在 我们已经录制了一段空间视频 接下来看看如何播放 第一步是空间素材的检测和载入 这一步可以通过几种不同的方法实现 包括 PhotosPicker、PhotoKit 和 AVAssetPlaybackAssistant
PhotosPicker API 会展示一个视图 来帮助用户从照片图库中选择素材 它现在支持筛选空间素材 在匹配的参数中 指定 .spatialMedia 它就可以对用户的图库进行筛选 仅显示空间照片和空间视频
如果你不需要用户进行输入 则可以使用 PhotoKit 以编程方式获取用户照片图库中的 所有空间素材 在构建 fetch 谓词时 只需将 spatialMedia 设置为 PHAssetMediaSubtype
还可以将 fetch 请求 修改成仅返回空间照片 或是仅返回空间视频
对于视频 也可以使用 AVAssetPlaybackAssistant 可以查询它的 playbackConfigurationOptions 来确认本地空间素材 如果你的 App 需要 从照片图库之外的来源载入视频 这个选项就会非常适合你
现在 我们可以载入空间内容了 我们来展示一下 同样 这一步也可以 通过几个不同的选项来实现
QuickLook 框架中的 PreviewApplication API 支持照片和视频 Javascript 中的 Element FullScreen API 支持从网页载入空间照片 AVKit 中的 AVPlayerViewController 支持空间视频
QuickLook 是在 visionOS 上 快速打开文件的系统标配方式 借助新的 PreviewApplication API 现在可以在你自己的 App 中 生成 QuickLook 场景 来托管你自己的内容 它同时支持空间照片和空间视频 并且和“照片”App 一样 采用全空间展示 有关 PreviewApplication API 的更多信息 请观看视频 “‘快速查看’新功能 在空间计算中的应用”
然后是 JavaScript 中的 Element FullScreen API 它支持在“Safari 浏览器”中 从网页打开空间照片 和 PreviewApplication 一样 照片会在新场景中打开 并附带有全空间展示 如需进一步了解 FullScreen API 请观看视频 “针对空间网页进行优化” 最后还有 AVPlayerViewController 这是我们的跨平台 API 用于播放各种类型的视频内容 同时支持 2D 和 3D 视频 所以如果你需要以一致的展示样式 同时显示两种类型的内容 这个选项就非常适合你 它还支持空间视频的 HTTP Live Streaming
有几点要牢记 使用 AVPlayerViewController 播放空间视频时 视频只有在全屏模式下 才会以 3D 形式显示 在嵌入展示中会显示 2D 形式 要让内容全屏显示 最简单的方法 是调整 AVPlayerViewController 的大小以匹配 WindowScene 的边框
请注意 即使在全屏模式下 AVPlayerViewController 也会使用 3D 视频展示 而不是空间视频展示 由于空间视频 构建在 MVHEVC 之上 因此它们也可以用作 3D 视频 AVPlayerViewController 就是这样处理它们的 如果 App 需要全空间展示 请改为使用 PreviewApplication
最后 在设计 App 时 请尽量减少直接放置在 视频顶部的 UI 元素数量 整洁的 UI 有助于 让观看者专注于内容 并最大限度降低发生景深冲突的风险 现在 我们已经了解了 记录、检测、载入 和展示空间媒体的多种方法 作为一名 App 开发者 你只需 使用我们刚刚介绍的 API 就能开始在你的 App 中 使用空间媒体
但对于想要创作自定空间媒体的用户 我们来深入了解一下这方面的知识
从文件格式层面来说 空间视频 是一种具有额外空间元数据的 立体 MV-HEVC 视频
空间照片是一种立体 HEIC 包含了位于一个 Stereo Group 的两个图像 以及一些空间元数据 要了解如何读取和写入这些文件 可以访问 developer.apple.com 查看两个示例代码项目 和一篇文章 接下来 我们深入了解一下 两种文件格式都常用的 空间元数据 空间元数据包含了以下几项信息 有投影 这一项定义了现实场景中的 对象与 图像像素值之间的关系 空间照片和视频始终 使用直线投影 然后还有基准线和视角 这几项描述了拍摄空间素材的 摄像头的物理属性 还有视差调整 这一项控制着 窗口式展示中的 3D 效果 此外 还有一些 关于应该如何创建 左侧和右侧图像的准则 由于这些图像特征 以像素形式储存在图像中 因此它们不是显式元数据栏位 而且在进行空间文件检查确认时 也不会强制执行这些特征 但是 了解这些特征仍然很重要 有助于采用正确的方法 来实现最佳观看体验
理想的图像特征包括 左侧和右侧图像应该经过立体校正 与光轴对齐 并且不存在垂直视差 新的技术术语可真不少 为了更好地理解它们 我们来看一个示例
这是蜂鸟 Howie 我们将会以它为对象拍摄空间照片 为了拍摄它 我使用两个 水平对齐的摄像头 来拍摄立体像对
两个摄像头同步拍摄 各拍摄一张照片 构成立体像对的 左侧图像和右侧图像 这两个摄像头都是针孔摄像头 因此通过直线投影成像 这意味着现实世界中的直线 在图像中也会显示为直线
第一个元数据栏位是基准线 有时称为轴间距离 它定义了两个摄像头中心 之间的水平距离 设置基准线的最佳距离 取决于具体用例 设置 64 毫米左右的值 可生成最接近人类视觉的图像 因为我们双眼的间距 往往就是这么大 不过较小的值有时效果很棒 尤其是在拍摄特写场景时 而非常大的基准线 用于拍出远处物体的景深 比如在立体风景摄影中 选择比较宽的基准线 代价就是由于夸大景深 而导致“微缩效应” 在这个示例中 我将一直使用 64 毫米的基线
下一项元数据是视角 这是每个摄像头 拍摄范围的水平角 单位是度 一般来说 视角值越高 沉浸感就越强 因为可以看到更完整的场景 但是 对于固定传感器来说 视角越大就意味着 会有越多内容 分布在相同数量的像素上 因此角分辨率也就越低 此外 由于通过直线投影成像 因此当视角大于 90 度时 角采样密度会变得效率低下 尤其是在边缘 因此建议最好不要 采用大于 90 度的视角 在这个示例中 我要使用 60 度的视角
接下来 我们来看看 各种图像特征 第一条准则指出 图像应该经过立体校正 这意味着两个摄像头的光轴 是平行的 如图中所示 并且两个图像位于同一平面上 如果摄像头的光轴 像图中所示这样并不平行 那么两个图像就不是共面的 因此没有经过立体校正 如果遇到这种情况 就必须采用 称为立体校正的图像处理技术 来校正图像畸变
接下来 我们要确保图像中心 与光学中心对齐 这意味着每个图像的中心像素 都与光轴对齐 这是针孔摄像头的默认设置 但是我们可能会在不经意间 以几种不易察觉的方式破坏这种对齐 为了了解这个问题 我们来 仔细看看我们的图像
这是我们的立体像对 其中图像中心与光轴对齐 但如果我们裁剪图像 中心像素就会改变 新的中心像素不再与光轴对齐 所以这个裁剪后的立体像对效果欠佳 同样 将图像左右移动 也会改变中心像素 从而打破这条准则 应用这些水平位移 是 3D 电影拍摄中的常见做法 用于实现艺术效果 但是不建议对空间媒体采取这一做法 要实现这些效果 可以使用视差调整元数据 我们稍后还会再谈到这一项 首先 我们需要确保没有垂直视差 要实现这一点 最简单的方法 就是拍摄左侧图像和右侧图像 并确定两个图像 共有的某个特征点 在这个示例中 特征点是蜂鸟的喙 然后在两个特征点之间 绘制一条直线 如果这条直线完全水平 就说明图像正确对齐 不存在垂直视差
这是另一个立体像对 其中摄像头没有对齐
我们可以通过标出特征点并加以连接 来检查是否存在垂直视差 在这里 特征点之间的连线 不是完全水平的 这意味着两个图像存在垂直视差 因此在佩戴 Vision Pro 观看时 可能会让人感到不适
我们来回顾一下到目前为止的进展 我们获得了左侧和右侧画面 并确保它们通过直线投影成像 经过立体校正、与光轴对齐 并且不存在垂直视差 我们也知道我们的 基准线和视角
接下来我们看看内容的渲染方式 这里就是奇迹发生的地方 认真完成的摄像头校准过程 也将在这里最终带来回报 借助所提供的信息 渲染器可以构建摄像头模型 这个模型定义了摄像头如何拍摄 现实世界中的对象
然后反向追踪这些光线 以按照正确的尺寸和距离 来渲染两个图像
佩戴 Vision Pro 观看时 向观看者的每只眼 各显示一个图像 视觉系统会将这两个图像 融合到 3D 场景中 让观看者感知到 3D 对象 如果使用准确的元数据 正确地构建了文件 并确保图像符合所有图像特征准则 观看者双眼感知到的图像 就会与摄像头拍摄到的 真实对象完全匹配 观看者用自己的双眼取代摄像头 身临其境地重温了那一刻的场景 以第一人称视角 “重温某一刻”的奇妙感受 就是这样产生的
空间照片在共享空间中 也同样极具感染力 在这个模式下 visionOS 会在窗口中 显示这些图像 在缩小场景的同时 仍然对场景进行立体渲染 这个模式的目标不是栩栩如生地 完美再现真实场景 而是提供能够在艺术上 和情感上引起共鸣的 3D 特效 为了确保观看者在观看窗口中 显示的内容时感到舒适 我们引入了最后一项空间元数据 视差调整 如果你熟悉立体摄影 就会知道 视差调整是提供给渲染器的提示 指出了零视差平面的放置位置 这项功能有时也称为会聚调整 或水平图像平移
其实它所表示的 只是一个水平偏移 以图像宽度的百分比进行编码 负值会将左侧画面向右推动 并将右侧画面向左推动 正值会将它们向相反的方向推动
当图像水平移动时 大脑感知的 3D 场景 会沿 z 轴 发生景深变化 负值会让内容 看起来更靠近观看者 而正值则会将内容推向更远的位置 设置这个值时要小心谨慎! 如果负值太大 可能会造成景深冲突 而如果正值太大 可能会导致双眼视线分散 特别是对于背景中的对象 这可能会让观看者感到不适 对于这个场景 我将选择一个值 来将蜂鸟放置在 窗口正面上方 即使只移动图像宽度的百分之几 也足以极大地改变 观看者对图像的感知 因此在选择视差调整的值时 请务必戴上 Vision Pro 进行测试 现在 我们可以添加水平视差调整了 完成这一步之后 我们就拥有了构建空间素材 所需的所有输入 最后只剩下一件事 那就是 将这些数据包装到恰当的文件格式中 立体 MV-HEVC 适用于视频 而立体 HEIC 适用于照片
现在 我们最终的作品可以 在 Apple Vision Pro 中正确渲染 并且无论在窗口式展示中 还是在完全沉浸模式下 都呈现出了令人惊叹的视觉效果
我们来总结一下 在这个讲座中 我们了解了 visionOS 上的 多种立体媒体类型: 3D 视频、Apple Immersive 视频 以及空间照片和视频 我们探索了如何 在 App 中拍摄、检测和显示 空间照片和视频 我们还了解了 如何创作自己的空间媒体 并利用准确的元数据 和精确对齐的图像 在 Vision Pro 中提供出色体验
以上就是今天的内容! 我迫不及待想要看到 大家使用空间媒体打造出 各种令人惊叹的全新体验
-
-
6:19 - Spatial video capture on iPhone 15 Pro
class CaptureManager { var session: AVCaptureSession! var input: AVCaptureDeviceInput! var output: AVCaptureMovieFileOutput! func setupSession() throws -> Bool { session = AVCaptureSession() session.beginConfiguration() guard let videoDevice = AVCaptureDevice.default( .builtInDualWideCamera, for: .video, position: .back ) else { return false } var foundSpatialFormat = false for format in videoDevice.formats { if format.isSpatialVideoCaptureSupported { try videoDevice.lockForConfiguration() videoDevice.activeFormat = format videoDevice.unlockForConfiguration() foundSpatialFormat = true break } } guard foundSpatialFormat else { return false } let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice) guard session.canAddInput(videoDeviceInput) else { return false } session.addInput(videoDeviceInput) input = videoDeviceInput let movieFileOutput = AVCaptureMovieFileOutput() guard session.canAddOutput(movieFileOutput) else { return false } session.addOutput(movieFileOutput) output = movieFileOutput guard let connection = output.connection(with: .video) else { return false } guard connection.isVideoStabilizationSupported else { return false } connection.preferredVideoStabilizationMode = .cinematicExtendedEnhanced guard movieFileOutput.isSpatialVideoCaptureSupported else { return false } movieFileOutput.isSpatialVideoCaptureEnabled = true session.commitConfiguration() session.startRunning() return true } }
-
9:13 - Observing spatial capture discomfort reasons
let observation = videoDevice.observe(\.spatialCaptureDiscomfortReasons) { (device, change) in guard let newValue = change.newValue else { return } if newValue.contains(.subjectTooClose) { // Guide user to move back } if newValue.contains(.notEnoughLight) { // Guide user to find a brighter environment } }
-
9:58 - PhotosPicker
import SwiftUI import PhotosUI struct PickerView: View { @State var selectedItem: PhotosPickerItem? var body: some View { PhotosPicker(selection: $selectedItem, matching: .spatialMedia) { Text("Choose a spatial photo or video") } } }
-
10:14 - PhotoKit - all spatial assets
import Photos func fetchSpatialAssets() { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate( format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.spatialMedia.rawValue] ) fetchResult = PHAsset.fetchAssets(with: fetchOptions) }
-
10:36 - AVAssetPlaybackAssistant
import AVFoundation extension AVURLAsset { func isSpatialVideo() async -> Bool { let assistant = AVAssetPlaybackAssistant(asset: self) let options = await assistant.playbackConfigurationOptions return options.contains(.spatialVideo) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。