WWDC Quick Look 💓 By SwiftGGTeam
Build compelling spatial photo and video experiences

Build compelling spatial photo and video experiences

观看原视频

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 的格式并设为 activeFormat06: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 深度偏移,负值让内容更近,正值推远。几百分比的偏移就会显著改变观感

图像要求:立体校正(光轴平行)、光轴对齐(图像中心与光轴中心对齐,不要裁剪或水平位移)、无垂直视差(同一特征点在左右图中的连线必须水平)。


核心启发

  1. 在现有相机应用中加一个”空间视频”开关:只需要切换 builtInDualWideCamera、选择支持空间视频的 format、设置 isSpatialVideoCaptureEnabled = true 三步改动。为什么值得做:用户用你的应用拍摄的内容会直接出现在 Vision Pro 的空间体验中,这是只有 iPhone 15 Pro 才有的差异化能力。怎么开始:基于 AVCam 示例代码项目,按上述三个步骤修改。

  2. 在相册浏览界面过滤空间内容:使用 PhotosPicker(matching: .spatialMedia) 或 PhotoKit 的 spatialMedia 子类型查询。为什么值得做:Vision Pro 用户的空间内容会越来越多,专门的入口比混合在全部内容里更容易发现。怎么开始:在现有照片选择器中加入 .spatialMedia 过滤条件。

  3. 用 QuickLook PreviewApplication 展示空间媒体:这是最简单的方案,几行代码就能获得与系统照片应用一致的空间呈现。为什么值得做:自己实现空间播放器需要处理立体渲染、沉浸模式切换等复杂逻辑,QuickLook 全部内置。怎么开始:导入 QuickLook 框架,创建 PreviewApplication 并传入空间媒体文件。

  4. 添加拍摄不适提示 UI:KVO 观察 spatialCaptureDiscomfortReasons,在 .subjectTooClose.notEnoughLight 时显示引导。为什么值得做:iPhone 屏幕是单目的,用户无法预览空间效果,拍到不适内容在 Vision Pro 回看时才发现就晚了。怎么开始:在相机预览界面叠加提示视图,参照系统相机应用的警告样式。


关联 Session

评论

GitHub Issues · utterances