WWDC Quick Look 💓 By SwiftGGTeam
Learn about Apple Immersive Video technologies

Learn about Apple Immersive Video technologies

观看原视频

Highlight

macOS 与 visionOS 26 新增 ImmersiveMediaSupport 框架,配合 AIVU 文件容器和 ASAF/APAC 音频格式,让第三方工具也能读写 Apple Immersive Video 的元数据。


核心内容

Apple Immersive Video(AIV)是 Apple Vision Pro 上画质最高的沉浸式视频格式,专为 Blackmagic URSA Cine Immersive 这类相机设计。这种相机出厂时就经过逐机标定(factory calibration),把每只立体镜头的曲率参数写进视频文件,渲染时再根据这些参数做参数化投影。问题在于,过去这套元数据只能由 Apple 内部工具处理:第三方非线性编辑软件、转码工具、流媒体平台拿到 AIV 素材后,无法读写其中的相机标定、shot flop、淡入淡出等关键信息,整条生产链卡在中间。

Blake 在这场 session 里宣布了打通这条链路的方案。macOS 与 visionOS 26 新增 ImmersiveMediaSupport 框架,提供 VenueDescriptor(多相机组合 + 边缘混合 mask + 背景环境)和 PresentationDescriptor(每帧的标定切换、shot flop、fade 等动态指令)两套数据类型;同时定义 AIVU(Apple Immersive Video Universal)文件容器,把视频轨、PresentationDescriptor、VenueDescriptor 打包成单一文件,可在 visionOS 的 Files app 里通过 Quick Look 直接预览。读写都走 AVFoundation:用 quickTimeMetadataAIMEData 标识符过滤元数据拿到 VenueDescriptor,用 quickTimeMetadataPresentationImmersiveMedia 拿到 PresentationDescriptor。写入则把投影类型设为 AppleImmersiveVideo,再用 AVAssetWriter 把元数据 mux 进去。音频侧,Apple 新出 ASAF(Apple Spatial Audio Format)做制作格式,APAC(Apple Positional Audio Codec)做分发格式,APAC 是 AIV 体验的强制要求。


详细内容

读取 AIVU 文件中的 VenueDescriptor06:23):

func readAIMEData(from aivuFile: URL) async throws -> VenueDescriptor? {
    let avAsset = AVURLAsset(url: aivuFile)
    let metadata = try await avAsset.load(.metadata)
    let aimeData = metadata.filter({ $0.identifier == .quickTimeMetadataAIMEData }).first
    if let dataValue = try await aimeData.load(.value) as? NSData {
        return try await VenueDescriptor(aimeData: dataValue as Data)
    }
    return nil
}

关键点:

  • AVURLAsset(url:) 把 AIVU 当成普通 QuickTime 资源加载。
  • load(.metadata) 异步取出整份资源级元数据。
  • quickTimeMetadataAIMEData 是 ImmersiveMediaSupport 新增的标识符,专门用来过滤 AIME(Apple Immersive Media Embedded)数据。
  • VenueDescriptor(aimeData:) 把原始 Data 反序列化为相机组合描述。

读取每帧的 PresentationDescriptor06:50):

func presentation(timedMetadata: [AVTimedMetadataGroup]) async throws ->   
[PresentationDescriptor] {
    var presentations: [PresentationDescriptor] = [] 
    for group in timedMetadata {
        for metadata in group.items {
            if metadata.identifier == .quickTimeMetadataPresentationImmersiveMedia {
                let data = try await metadata.load(.dataValue) {
                    presentations.append(
                        try JSONDecoder().decode(PresentationDescriptor.self, from: data)
                    )
                }
            }
        }
    }
    return presentations
}

关键点:

  • 入参是 AVTimedMetadataGroup 数组,每组对应一段视频帧的 PTS 区间。
  • 通过 quickTimeMetadataPresentationImmersiveMedia 标识符过滤 Presentation 命令。
  • 命令以 JSON 编码,用 JSONDecoder 解出 PresentationDescriptor,里面带着该时间点的相机标定切换、shot flop、fade 等指令。
  • 详细取 timed metadata 的方式参考 AVPlayerItemMetadataOutput

写入 VenueDescriptor 到 AVMetadataItem07:52):

func getMetadataItem(from metadata: VenueDescriptor) async throws -> AVMetadataItem {
    let aimeData = try await metadata.aimeData
    let aimeMetadataItem = AVMutableMetadataItem()
    aimeMetadataItem.identifier = .quickTimeMetadataAIMEData
    aimeMetadataItem.dataType = String(kCMMetadataBaseDataType_RawData)
    aimeMetadataItem.value = aimeData as NSData
        
    return aimeMetadataItem
}

关键点:

  • metadata.aimeData 把 VenueDescriptor 序列化成原始字节。
  • dataType = kCMMetadataBaseDataType_RawData 告诉容器这是原始二进制,避免被误处理为字符串。
  • 这一项作为资源级元数据写入,配合 AVAssetWriter 即可生成可播放的 AIVU。

写入按帧时序的 PresentationDescriptor08:02):

func getMetadataItem(reader: PresentationDescriptorReader, 
                     time: CMTime, frameDuration: CMTime) -> AVMetadataItem? {
    let commands = reader.outputPresentationCommands(for: time) ?? []
    if commands.isEmpty { return nil }

    let descriptor = PresentationDescriptor(commands: commands)
    let encodedData = try JSONEncoder().encode(descriptor)
    let presentationMetadata = AVMutableMetadataItem()
    presentationMetadata.identifier = .quickTimeMetadataPresentationImmersiveMedia
    presentationMetadata.dataType = String(kCMMetadataBaseDataType_RawData)
    presentationMetadata.value = encodedData as NSData
    presentationMetadata.time = time
    presentationMetadata.duration = frameDuration
    
    return presentationMetadata
}

关键点:

  • PresentationDescriptorReader.outputPresentationCommands(for:) 按时间取出该帧需要执行的命令序列。
  • 命令为空时返回 nil,避免写入冗余的元数据条目。
  • timeduration 必须对齐到视频帧 buffer 的 PTS 与帧长,播放器据此触发标定切换或 shot flop。
  • 编码沿用 JSON,写入侧与读取侧对称。

校验 AIVU 文件08:20):

func validAIVU(file aivuFile: URL) async throws -> Bool { 
    return try await AIVUValidator.validate(url: aivuFile)
}

关键点:

  • AIVUValidator.validate(url:) 在写入流水线尾部跑一次,提前发现元数据缺失或不一致。
  • 校验失败抛错;校验通过返回 true。

HLS 发布要点09:31 起):

  • 推荐参数:每只眼 4320×4320、90fps、P3-D65-PQ 色彩空间、MV-HEVC。
  • 码率:平均 25–100 Mbps,峰值 50–150 Mbps,分层时保持分辨率与帧率一致,只在码率维度做取舍。
  • venueDescriptor.save(to:) 单独导出一个 .aime 文件,放在 HLS playlist 旁边。
  • multivariant playlist 需 HLS version ≥ 12,标注 venue description data ID、content type=fully immersive、stereo video layout、AppleImmersiveVideo 投影、APAC 音频。
  • 切片时务必保留 PresentationDescriptor 所在的 timed metadata track,否则播放端无法在帧级别切换标定。

预览链路ImmersiveMediaRemotePreviewSenderImmersiveMediaRemotePreviewReceiver 把低码率帧从 Mac 推到 Vision Pro,配合自定义 compositor 在 visionOS app 里实时显示,专门服务编辑期。

音频部分11:21 起):ASAF 基于 broadcast Wave + 线性 PCM + 元数据,渲染完全自适应,根据物体和听者位置实时计算,没有烘焙。APAC 是 ASAF 的 mp4 分发编码,64 kbps 起步,支持 Channels、Objects、HOA、Dialogue、Binaural、交互元素,watchOS 之外的全平台可播。制作工具是 Apple Pro Tools 插件和 DaVinci Resolve Studio Editor。HLS 侧需要在 media tag 里写音频通道信息,并在 stream info tag 里指定 APAC 编解码器。


核心启发

  • 做什么:在自家相册或视频管理 app 里加一条 AIVU 直读管线。

    • 为什么值得做:visionOS 26 起 AIVU 已是 Apple 官方推荐的单文件交付格式,Files + Quick Look 已原生支持。先把它当一等公民处理,用户从同事或 NLE 拿到 AIVU 就能直接消费。
    • 怎么开始:AVURLAsset 加载 → 过滤 quickTimeMetadataAIMEData 取 VenueDescriptor → 监听 timed metadata group 取 PresentationDescriptor → 用系统播放栈渲染。读写代码大约百行内。
  • 做什么:给 NLE/转码工具加一个 “Export as AIVU” 按钮。

    • 为什么值得做:AIV 元数据一旦在中间环节丢失,下游就拿不到正确的相机标定,画面会扭曲。把 AIVU 作为统一的中间交付格式可以保住元数据。
    • 怎么开始:用 AVAssetWriter,把投影类型设为 AppleImmersiveVideo,VenueDescriptor 走资源级 metadata,PresentationDescriptor 走 timed metadata,写完调用 AIVUValidator.validate(url:) 自检。
  • 做什么:搭一条 Mac→Vision Pro 的实时预览链路。

    • 为什么值得做:剪辑期的素材通常没经过完整压制,开发者过去只能导出再导入,反馈周期太长。ImmersiveMediaRemotePreviewSender/Receiver 把这一步压到帧级。
    • 怎么开始:Mac 端用 Sender 推低码率帧,Vision Pro 端写一个带自定义 compositor 的 visionOS app 接 Receiver,预览质量牺牲换取低延迟。
  • 做什么:流媒体后端补齐 HLS 的 AIV 字段。

    • 为什么值得做:Apple Vision Pro 对 AIV HLS 有硬要求(version 12、AIME 旁路文件、fully immersive content type、APAC 音频)。任何一项缺失播放器就会拒绝。
    • 怎么开始:先按推荐 tier(25/50/100 Mbps 平均,50/100/150 Mbps 峰值)切片,保留 timed metadata track,把 venueDescriptor.save(to:) 出的 .aime 文件放进 playlist 目录,再在 multivariant playlist 加好对应 tag。
  • 做什么:声音侧补齐 ASAF→APAC 的工作流。

    • 为什么值得做:APAC 是 AIV 体验的强制项,且 64 kbps 起就能跑出沉浸式空间音频,对带宽友好。
    • 怎么开始:制作端选 Apple Pro Tools 插件或 DaVinci Resolve Studio Editor 输出 ASAF(broadcast Wave + 元数据),再编码为 mp4 APAC,HLS 端在 media tag 标注通道信息、stream info tag 指定 APAC 编码。

关联 Session

评论

GitHub Issues · utterances