Highlight
iPhone 15 Pro 的广角和超广角摄像头横屏时处于同一水平基线,配合 AVFoundation 现有 API 三个改动即可录制空间视频,同步、校准、编码和元数据写入全部由系统处理。
核心内容
Vision Pro 上有三种立体视频体验:3D Video 在平面屏幕上呈现深度感,适合电影;Spatial Video 通过窗口播放,沉浸模式下内容与真实世界融合,适合日常记录;Apple Immersive Video 用 180 度 8K 加 Spatial Audio,适合专业制作。三者在窗口模式和沉浸模式下的呈现方式各不相同——3D 视频会嵌入环境中的大屏幕,空间视频会展开到与真实物体等比例的沉浸视图,Apple Immersive 则完全包裹观众。
过去一年,空间媒体录制和播放都局限在系统应用内。今年 Apple 把这些能力开放给了开发者,且没有引入新框架。录制用 AVFoundation,检测用 PhotoKit,播放用 QuickLook——全部集成在已有框架中,改几行代码就能接入。
录制的硬件基础是 iPhone 15 Pro 的摄像头布局:广角和超广角摄像头从对角排列改为并排,横屏时处于同一水平基线,与人眼布局类似。API 层面只需三步:切换到 builtInDualWideCamera,选择支持空间视频的 format,开启 isSpatialVideoCaptureEnabled。播放和检测方面,PhotosPicker 新增 .spatialMedia 过滤,PhotoKit 支持 spatialMedia 子类型查询,AVAssetPlaybackAssistant 可以检测任意本地文件是否为空间视频。展示方面,QuickLook 的 PreviewApplication API 支持完整的空间呈现,Safari 的 Element FullScreen API 支持网页端空间照片,AVPlayerViewController 支持空间视频的 3D 播放。
详细内容
录制空间视频
核心代码来自官方示例(06:19):
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
}
}
关键点:
- 使用
builtInDualWideCamera而非systemPreferredCamera,因为空间视频需要广角和超广角同时工作(06:22) - 遍历
videoDevice.formats找到isSpatialVideoCaptureSupported为 true 的格式并设为activeFormat(06:34) preferredVideoStabilizationMode设为.cinematicExtendedEnhanced,使用更长的前视窗口和更多裁剪来获得更平滑的画面(07:40)isSpatialVideoCaptureEnabled设为 true 开启空间视频录制,仅 iPhone 15 Pro 支持(06:56)- 同步、校准、编码和元数据写入全由系统处理,开发者拿到的是完整的空间视频文件
拍摄不适监测
广角和超广角是不同类型的摄像头,弱光下噪点水平不同,拍摄距离过近时会出现对焦不匹配。iPhone 屏幕是单目的,用户在拍摄时无法察觉这些问题。Apple 新增了 spatialCaptureDiscomfortReasons 属性(09:13):
let observation = videoDevice.observe(\.spatialCaptureDiscomfortReasons) { (device, change) in
guard let newValue = change.newValue else { return }
if newValue.contains(.subjectTooClose) {
// 提示用户后退
}
if newValue.contains(.notEnoughLight) {
// 提示用户寻找更亮的环境
}
}
关键点:
- 使用 KVO 观察
spatialCaptureDiscomfortReasons变化 .subjectTooClose表示拍摄对象过近,某个摄像头无法对焦.notEnoughLight表示光线不足,两个摄像头噪点差异可能导致观看不适- 建议在 UI 中显示引导提示,类似系统相机应用的警告
检测空间内容
三种方式检测空间媒体:
PhotosPicker 支持过滤空间内容(09:58):
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")
}
}
}
关键点:
matching: .spatialMedia过滤只显示空间照片和视频- 适合需要用户手动选择内容的场景
PhotoKit 直接查询所有空间资源(10:14):
import Photos
func fetchSpatialAssets() {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(
format: "(mediaSubtypes & %d) != 0",
argumentArray: [PHAssetMediaSubtype.spatialMedia.rawValue]
)
fetchResult = PHAsset.fetchAssets(with: fetchOptions)
}
关键点:
- 使用
PHAssetMediaSubtype.spatialMedia作为子类型筛选条件 - 可以进一步过滤只返回空间照片或空间视频
- 适合不需要用户交互的程序化查询
AVAssetPlaybackAssistant 检测本地文件(10:36):
import AVFoundation
extension AVURLAsset {
func isSpatialVideo() async -> Bool {
let assistant = AVAssetPlaybackAssistant(asset: self)
let options = await assistant.playbackConfigurationOptions
return options.contains(.spatialVideo)
}
}
关键点:
- 查询
playbackConfigurationOptions是否包含.spatialVideo - 适合从相册以外来源加载视频的场景
展示空间内容
三种展示方式各有侧重:
- QuickLook PreviewApplication:支持空间照片和视频,使用完整的空间呈现,与系统照片应用一致。适合需要标准空间播放体验的场景(10:57)
- Safari Element FullScreen API:从网页打开空间照片,在新场景中展示完整空间呈现(11:36)
- AVPlayerViewController:支持 2D 和 3D 视频统一播放,支持 HLS 流媒体。注意空间视频在此只会以 3D 视频方式呈现(不会使用空间视频的沉浸视图),如需完整空间呈现应使用 PreviewApplication(11:54)
空间元数据
空间视频是带空间元数据的 MV-HEVC 文件,空间照片是带空间元数据的立体 HEIC 文件。元数据包含(13:39):
- 投影类型:始终为 rectilinear(直线投影)
- 基线(Baseline):两个摄像头中心的水平距离,约 64mm 接近人眼间距。更大基线会带来微缩效果
- 视场角(Field of View):水平角度,越大越沉浸但分辨率越低。超过 90 度不推荐
- 视差调整(Disparity Adjustment):控制窗口模式下的 3D 深度偏移,负值让内容更近,正值推远。几百分比的偏移就会显著改变观感
图像要求:立体校正(光轴平行)、光轴对齐(图像中心与光轴中心对齐,不要裁剪或水平位移)、无垂直视差(同一特征点在左右图中的连线必须水平)。
核心启发
-
在现有相机应用中加一个”空间视频”开关:只需要切换
builtInDualWideCamera、选择支持空间视频的 format、设置isSpatialVideoCaptureEnabled = true三步改动。为什么值得做:用户用你的应用拍摄的内容会直接出现在 Vision Pro 的空间体验中,这是只有 iPhone 15 Pro 才有的差异化能力。怎么开始:基于 AVCam 示例代码项目,按上述三个步骤修改。 -
在相册浏览界面过滤空间内容:使用
PhotosPicker(matching: .spatialMedia)或 PhotoKit 的spatialMedia子类型查询。为什么值得做:Vision Pro 用户的空间内容会越来越多,专门的入口比混合在全部内容里更容易发现。怎么开始:在现有照片选择器中加入.spatialMedia过滤条件。 -
用 QuickLook PreviewApplication 展示空间媒体:这是最简单的方案,几行代码就能获得与系统照片应用一致的空间呈现。为什么值得做:自己实现空间播放器需要处理立体渲染、沉浸模式切换等复杂逻辑,QuickLook 全部内置。怎么开始:导入 QuickLook 框架,创建
PreviewApplication并传入空间媒体文件。 -
添加拍摄不适提示 UI:KVO 观察
spatialCaptureDiscomfortReasons,在.subjectTooClose或.notEnoughLight时显示引导。为什么值得做:iPhone 屏幕是单目的,用户无法预览空间效果,拍到不适内容在 Vision Pro 回看时才发现就晚了。怎么开始:在相机预览界面叠加提示视图,参照系统相机应用的警告样式。
关联 Session
- Enhance the immersion of media viewing in custom environments — 3D 视频在自定义环境中 docking 沉浸播放的实现
- What’s new in Quick Look for visionOS — PreviewApplication API 的详细用法
- Optimize for the spatial web — 网页端 FullScreen API 展示空间照片
- Bring your iOS or iPadOS game to visionOS — 实时 3D 内容在 visionOS 上的立体渲染
评论
GitHub Issues · utterances