WWDC Quick Look 💓 By SwiftGGTeam
Learn about the Apple Projected Media Profile

Learn about the Apple Projected Media Profile

观看原视频

Highlight

APMP 通过在 QuickTime/MP4 的 vexu box 里新增 projection、lens collection、view packing 三类盒子,把 180/360/广角投影信号加进消费级视频文件,并在 visionOS 26 的 AVFoundation 里打通了识别、转换、读写、编辑和 HLS 分发链路。


核心内容

之前要让 GoPro、Insta360、Canon EOS VR 拍出来的 180/360/广角视频在 Apple Vision Pro 上正确呈现,开发者得自己解析 Spherical Metadata V1/V2,再判断帧封装是 side-by-side 还是 over-under,最后把每只眼睛的画面手动喂给播放器。投影类型、镜头畸变、立体视图三件事散落在不同字段里,没有统一的容器表示,工具链只能各家自定义。

Core Media Spatial Technologies 团队的工程师 Jon 在这场 session 介绍了 Apple Projected Media Profile(APMP),把这套表示收敛进 QuickTime/MP4 的标准结构。visionOS 26 上 APMP 用一组 Video Extended Usage(vexu)扩展 box 描述非矩形投影:投影类型(equirectangular / half-equirectangular / parametricImmersive / appleImmersiveVideo)、镜头内参与畸变参数、左右眼帧封装方式。AVFoundation、Core Media、Video Toolbox、HLS 工具链都同步更新,识别、转换、读写、编辑、分发可以走同一套 API。配套的 Apple Positional Audio Codec(APAC)支持 1/2/3 阶 ambisonic 编码,把球面声场和球面视频拼成完整的沉浸体验。


详细内容

投影种类(01:31)。 2D/3D/Spatial 走 rectilinear;180 度走 half-equirectangular(X 轴映射 -90° 到 +90°);360 度走 equirectangular(X 轴映射 -180° 到 +180°,Y 轴映射 -90° 到 +90°);广角/鱼眼走 ParametricImmersive,包含 3×3 相机内参矩阵 K(焦距、光学中心、skew)以及径向畸变、切向畸变、projection offset、radial angle limit、lens frame adjustments 等参数,用来矫正广角镜头的桶形畸变。

容器中的信号(04:33)。 在 vexu 下新增三个 box:

  • Projection box:标记投影类型。
  • Lens Collection box:装 ParametricImmersive 的 intrinsics、extrinsics 和畸变参数。
  • View Packing box:标 side-by-side 或 over-under。

最小的单目 360 文件只需要一个 projection box;立体 180 文件需要 stereo view box 加 half-equirectangular projection box。完整规范见 developer.apple.com 上的《QuickTime and ISO Base Media File Formats and Spatial and Immersive Media》。

识别旧的 Spherical Metadata(08:58)。 AVFoundation 新增 AVURLAssetShouldParseExternalSphericalTagsKey,让系统把 Spherical Metadata V1/V2 当作 APMP 来读:

import AVFoundation

func wasConvertedFromSpherical(url: URL) -> Bool {
    let assetOptions = [AVURLAssetShouldParseExternalSphericalTagsKey: true]
    let urlAsset = AVURLAsset(url: url, options: assetOptions)

    let track = try await urlAsset.loadTracks(withMediaType: .video).first!
    let formatDescription = try await videoTrack.load(.formatDescriptions).first

    let wasConvertedFromSpherical = formatDescription.extensions[.convertedFromExternalSphericalTags]
    return wasConvertedFromSpherical
}

关键点:

  • AVURLAssetShouldParseExternalSphericalTagsKey: true:让 AVURLAsset 把球面元数据合成成 APMP 格式描述扩展,下游 API 就当 APMP 处理。
  • formatDescription.extensions[.convertedFromExternalSphericalTags]:判断这个 asset 是不是从球面元数据合成出来的,方便 UI 上做特别提示。

广角内容上抬到 ParametricImmersive(09:54)。 ImmersiveMediaSupport 框架内置 GoPro HERO 13、Insta360 Ace Pro 2 等机型的镜头参数:

import ImmersiveMediaSupport

func upliftIntoParametricImmersiveIfPossible(url: URL) -> AVMutableMovie {
    let movie = AVMutableMovie(url: url)

    let assetInfo = try await ParametricImmersiveAssetInfo(asset: movie)
    if (assetInfo.isConvertible) {
        guard let newDescription = assetInfo.requiredFormatDescription else {
            fatalError("no format description for convertible asset")
        }
        let videoTracks = try await movie.loadTracks(withMediaType: .video)
        guard let videoTrack = videoTracks.first,
              let currentDescription = try await videoTrack.load(.formatDescriptions).first
        else {
            fatalError("missing format description for video track")
        }
        videoTrack.replaceFormatDescription(currentDescription, with: newDescription)
    }
    return movie
}

关键点:

  • ParametricImmersiveAssetInfo(asset:):探测视频是否来自已知的广角相机型号。
  • assetInfo.isConvertible:识别成功就为 true。
  • requiredFormatDescription:拿到一个带 ParametricImmersive 投影、内参和畸变参数的新格式描述。
  • replaceFormatDescription(_:with:):把视频轨上的格式描述替换掉,整段视频对系统而言就是 wide-FOV APMP。

识别非矩形投影(10:58)。AVAssetPlaybackAssistant 拿到 playbackConfigurationOptions,检查 .nonRectilinearProjection.appleImmersiveVideo 两个选项即可分别判断 APMP 与 Apple Immersive Video。

精细判断 projectionKind 和 viewPackingKind(11:22)。 当 mediaCharacteristics 含 indicatesNonRectilinearProjection 时,从 formatDescription 取 .projectionKind(equirectangular / halfEquirectangular / parametricImmersive / appleImmersiveVideo)和 .viewPackingKind(sideBySide / overUnder),决定渲染管线如何处理。

立体编辑用 CMTaggedDynamicBuffer(12:51)。 AVVideoComposition 加了 outputBufferDescriptionfinish(withComposedTaggedBuffers:)

var config = try await AVVideoComposition.Configuration(for: asset)
config.outputBufferDescription = [[.stereoView(.leftEye)], [.stereoView(.rightEye)]]
let videoComposition = AVVideoComposition(configuration: config)

关键点:

  • outputBufferDescription:声明 compositor 会输出左右眼两个 tagged buffer,必须在 composition 启动前定义。
  • [.stereoView(.leftEye)] / [.stereoView(.rightEye)]:用 CM Tag 标识每只眼睛。

写入立体对(13:01)。startRequest 里给左右眼分别构造 CMTaggedDynamicBuffer,每个 buffer 带 .videoLayerID.stereoView 两个 CM Tag,最后 asyncVideoCompositionRequest.finish(withComposedTaggedBuffers:)

写入 APMP 文件(13:18)。AVAssetWriter 时,把投影信号塞进 AVVideoCompressionPropertiesKey 字典里的 CompressionPropertyKey.kind,AVAssetWriterInput 就会写出带 APMP 信号的 vexu box。

发布参数(13:51)。 HEVC Main / Main 10、4:2:0 色度子采样、Rec.709 或 P3-D65 色彩原色。单目 7680×3840、双目每眼 4320×4320,10-bit 8K 单目或 4K 双目建议 30fps,峰值码率不超过 150 Mbps。MV-HEVC 立体配置详见《Apple HEVC Stereo Video Interoperability Profile》。

AVQT(14:50)。 Advanced Video Quality Tool 加了 equirectangular / half-equirectangular 投影感知,可以在等距矩形展开图上更准确地评估压缩质量,还能用于 HLS 码率梯子调优。

HLS(15:25)。 EXT-X-STREAM-INFORMATION 新增 REQ-VIDEO-LAYOUT 属性同时表达 stereo 和投影类型;map segment 的 formatDescription 也必须带上对应的 projection 和 stereo view 扩展,光在 manifest 里写不算数。

APAC(16:14)。 Apple Positional Audio Codec 支持 1 阶(4 通道)、2 阶(9 通道)、3 阶(16 通道)ambisonic。除 watchOS 外所有 Apple 平台可解码;iOS、macOS、visionOS 通过 AVAssetWriter 内置编码器;推荐码率 1 阶 384 kbps、3 阶 768 kbps。可与 APMP 视频共同走 HLS 分段流式分发。


核心启发

1. 给 UGC 沉浸视频 App 接入 APMP 读写。 为什么值得做:visionOS 26 的播放、剪辑、Quick Look 都按 APMP 信号判断投影方式,App 只要写出合规文件,就能直接被 AirDrop、iCloud、Photos 当作沉浸内容处理。怎么开始:用 AVAssetWriter + AVVideoCompressionPropertiesKeyCompressionPropertyKey.kind 写 equirectangular 或 halfEquirectangular,立体内容再加 stereo view 扩展。

2. 接入旧 Spherical Metadata 的迁移路径。 为什么值得做:用户现存的 GoPro MAX、Insta360 X5 文件大多是 Spherical V1/V2,直接拒收会让导入率掉一截。怎么开始:构造 AVURLAsset 时加 AVURLAssetShouldParseExternalSphericalTagsKey: true,再用 convertedFromExternalSphericalTags 标志在 UI 上提示「已识别为 360 视频」,重要素材调 avconvert 或自写转换脚本永久写回 APMP。

3. 广角动作相机内容做 ParametricImmersive 上抬。 为什么值得做:GoPro HERO 13、Insta360 Ace Pro 2 的视频原本只是 2D 广角,不上抬就当普通矩形播放,浪费了 visionOS 的沉浸感。怎么开始:用 ImmersiveMediaSupportParametricImmersiveAssetInfo,对 isConvertible 为 true 的 asset 调 replaceFormatDescription,把内参和畸变写进格式描述。

4. 立体剪辑流水线接 CMTaggedDynamicBuffer。 为什么值得做:之前自定义 compositor 只能输出单 buffer,要做立体合成只能拼帧;现在 AVVideoComposition 直接传左右眼两块 tagged buffer,渲染器和导出器全程保持立体语义。怎么开始:在 AVVideoComposition.Configuration 上设 outputBufferDescription = [[.stereoView(.leftEye)], [.stereoView(.rightEye)]],在 compositor 里用 CMTaggedDynamicBuffer.videoLayerID.stereoView 两个 tag,最后 finish(withComposedTaggedBuffers:)

5. 沉浸视频音频迁移到 APAC + ambisonic。 为什么值得做:传统立体声跟着头转就破坏了沉浸感;ambisonic 把整个声场编进球面谐波,配合 Vision Pro 的头追播放才能定位声音方向。怎么开始:录制时用 4 麦或更多麦的 ambisonic 麦克风,编码用 AVAssetWriter 的 APAC outputSettings,1 阶 384 kbps 起步,3 阶 768 kbps 封顶;HLS 分发可以和 APMP 视频走同一个清单。


关联 Session

评论

GitHub Issues · utterances