WWDC Quick Look 💓 By SwiftGGTeam
Immerse your app in Spatial Audio

Immerse your app in Spatial Audio

观看原视频

Highlight

Apple 在 AVFoundation 和 WebKit 播放路径中加入空间音频支持,开发者可以用多声道 HLS、空间化格式控制和音频路由检测,为媒体 App 提供带头部追踪和带宽自适应的沉浸式播放体验。

核心内容

01:32)以前,移动设备上的视频声音大多停留在立体声。耳机里的左右声道会跟着头一起移动,声音像贴在头里。用户看电影时转头,声场也跟着转,影院里的固定声场体验就消失了。

02:14)Spatial Audio(空间音频)解决的是这个问题。它用心理声学技术生成虚拟声场。使用支持空间音频的耳机时,系统会比较播放设备和耳机里的惯性测量数据,得到用户的头部姿态,再动态调整渲染,让声场保持在画面周围。

02:49)对媒体服务来说,最直接的入口是多声道内容。Apple 明确说,如果你的 HLS(HTTP Live Streaming,HTTP 直播流)变体已经引用了多声道音频 alternate,发布这些内容后,App 默认就可以启用空间音频,很多情况下无需改代码。

05:39)多声道音频会占用更高码率。网络紧张时,系统会把音频降级到立体声上混处理,仍保留空间化体验。带宽恢复后,再回到完整多声道空间音频。开发者需要保证立体声和多声道 rendition 的音量一致,并提供 DRC(Dynamic Range Control,动态范围控制)和 dialnorm 元数据。

06:35)如果 App 需要控制空间化策略,AVFoundation 提供了 AVAudioSpatializationFormats。它可以允许单声道和立体声空间化、允许多声道空间化、允许两者都空间化,也可以设为 0 来关闭空间化。

详细内容

选择允许空间化的音频格式

06:55)Apple 提供 AVAudioSpatializationFormats(空间化音频格式)来描述哪些源音频可以被空间化。这个类型是 OptionSet,适合表达多个格式组合。

import AVFoundation

let formats: AVAudioSpatializationFormats = .monoStereoAndMultichannel

print(formats.contains(.monoAndStereo))
print(formats.contains(.multichannel))

关键点:

  • import AVFoundation 引入本 session 讨论的播放 API。
  • AVAudioSpatializationFormats 是空间化格式集合。
  • .monoStereoAndMultichannel 允许单声道、立体声和多声道源音频参与空间化。
  • contains(.monoAndStereo) 检查集合是否包含单声道和立体声空间化。
  • contains(.multichannel) 检查集合是否包含多声道空间化。

对应的 API 形状如下。

public struct AVAudioSpatializationFormats : OptionSet {

    public init(rawValue: UInt)

    public static var monoAndStereo: AVAudioSpatializationFormats { get }

    public static var multichannel: AVAudioSpatializationFormats { get }

    public static var monoStereoAndMultichannel: AVAudioSpatializationFormats { get }
}

关键点:

  • OptionSet 表示它可以组合多个选项。
  • monoAndStereo 允许单声道和立体声源音频空间化。
  • multichannel 允许多声道源音频空间化。
  • monoStereoAndMultichannel 是前两类能力的组合。

在播放器上设置空间化策略

07:21)空间化格式要设置到播放对象上。Session 点名了两条路径:AVPlayerItemAVSampleBufferAudioRenderer。2021 年秋季系统开始,后者也支持这个控制点。

import AVFoundation

let url = URL(string: "https://example.com/movie.m3u8")!
let playerItem = AVPlayerItem(url: url)

playerItem.allowedAudioSpatializationFormats = .multichannel

let player = AVPlayer(playerItem: playerItem)
player.play()

关键点:

  • URL 指向 HLS 媒体资源,示例使用 .m3u8
  • AVPlayerItem 承载要播放的媒体。
  • allowedAudioSpatializationFormats 指定允许空间化的源音频类型。
  • .multichannel 表示只允许多声道源音频进入空间化处理。
  • AVPlayer 使用这个 playerItem 开始播放。

Session 中展示的属性入口如下。

@available(macOS 11.0, *)
var allowedAudioSpatializationFormats: Int32

关键点:

  • allowedAudioSpatializationFormats 是控制空间化策略的属性。
  • 这个属性用于 AVPlayerItem,2021 年秋季系统也用于 AVSampleBufferAudioRenderer
  • 传入 0 可以抑制音频空间化,这一点来自 session 对四种取值后的补充说明。

检查当前音频路由是否启用空间音频

08:21)App 不能只看内容是否有多声道。用户当前的音频路由也要支持空间音频,并且用户允许启用。iOS 15 起,AVAudioSessionPortDescription 提供 isSpatialAudioEnabled

import AVFoundation

let session = AVAudioSession.sharedInstance()
let outputs = session.currentRoute.outputs

for output in outputs {
    if #available(iOS 15.0, *) {
        print(output.portName, output.isSpatialAudioEnabled)
    }
}

关键点:

  • AVAudioSession.sharedInstance() 获取当前 App 的音频会话。
  • currentRoute.outputs 返回当前输出路由,例如耳机或内建扬声器。
  • #available(iOS 15.0, *) 对齐 isSpatialAudioEnabled 的系统版本要求。
  • isSpatialAudioEnabled 同时表达两件事:端口有能力渲染空间音频,用户也允许使用。
  • portName 便于调试时看到当前输出设备。

对应的 API 形状如下。

@available(iOS 6.0, *)
class AVAudioSessionPortDescription : NSObject {

  @available(iOS 15.0, *)
  var isSpatialAudioEnabled: Bool { get }

}

关键点:

  • AVAudioSessionPortDescription 描述音频会话中的端口。
  • isSpatialAudioEnabled 从 iOS 15 开始可用。
  • 这个属性只读,App 用它判断当前路由状态。

监听空间播放能力变化

08:35)用户可以在控制中心或蓝牙设置里改变空间音频偏好。App 需要监听 spatialPlaybackCapabilitiesChangedNotification,并从通知里读取 AVAudioSessionSpatialAudioEnabledKey

import AVFoundation

let center = NotificationCenter.default

if #available(iOS 15.0, *) {
    center.addObserver(
        forName: AVAudioSession.spatialPlaybackCapabilitiesChangedNotification,
        object: AVAudioSession.sharedInstance(),
        queue: .main
    ) { notification in
        let enabled = notification.userInfo?[AVAudioSessionSpatialAudioEnabledKey] as? Bool
        print(enabled as Any)
    }
}

关键点:

  • NotificationCenter.default 用来注册系统通知观察者。
  • spatialPlaybackCapabilitiesChangedNotification 在空间播放能力变化时发出。
  • object 指向当前 AVAudioSession
  • queue: .main 让回调在主队列执行,适合更新 UI。
  • AVAudioSessionSpatialAudioEnabledKey 用来从 userInfo 取出空间音频启用状态。

Session 中展示的 API 如下。

extension AVAudioSession {
  @available(iOS 15.0, *)
  class let spatialPlaybackCapabilitiesChangedNotification: NSNotification.Name
}

@available(iOS 15.0, *)
let AVAudioSessionSpatialAudioEnabledKey: String

关键点:

  • 通知定义在 AVAudioSession 扩展上。
  • key 是字符串常量,用于读取通知携带的状态。
  • 这条通知覆盖用户在控制中心和蓝牙设置中修改空间音频偏好的场景。

向系统声明 App 支持多声道内容

09:01)如果 App 使用 AVSampleBufferAudioRenderer 或自定义播放路径,需要告诉系统自己可以提供多声道内容。入口是 setSupportsMultichannelContent(_:)

import AVFoundation

let audioSession = AVAudioSession.sharedInstance()

if #available(iOS 15.0, *) {
    do {
        try audioSession.setSupportsMultichannelContent(true)
        print(audioSession.supportsMultichannelContent)
    } catch {
        print(error)
    }
}

关键点:

  • AVAudioSession.sharedInstance() 代表当前 App 的音频会话。
  • setSupportsMultichannelContent(true) 声明 App 或服务可以提供多声道内容。
  • 这个声明会用于系统提示,让用户知道在网络条件允许且处理方式启用时,可以获得空间体验。
  • supportsMultichannelContent 读取当前声明状态。
  • do/catch 处理这个 throwing 方法可能抛出的错误。

Session 中展示的 API 如下。

extension AVAudioSession {
  @available(iOS 15.0, *)
  func setSupportsMultichannelContent(_ inValue: Bool) throws
  @available(iOS 15.0, *)
  var supportsMultichannelContent: Bool { get }
}

关键点:

  • 方法和属性都从 iOS 15 开始可用。
  • setSupportsMultichannelContent(_:) 是声明入口。
  • supportsMultichannelContent 是读取入口。
  • 如果 App 使用 AVPlayer,session 说明这些 indication 会由系统管理。

WebKit 和系统版本支持范围

09:20)空间音频支持覆盖多个系统版本。macOS Catalina、iOS 13、iPadOS 13 已经通过内建扬声器支持 AVPlayerItem 和 WebKit video tag。macOS Big Sur、iOS 14、iPadOS 14 加入 AirPods Pro 和 AirPods Max 的头部追踪耳机支持。

import AVFoundation

let item = AVPlayerItem(url: URL(string: "https://example.com/movie.m3u8")!)
item.allowedAudioSpatializationFormats = .monoStereoAndMultichannel

关键点:

  • AVPlayerItem 是跨系统支持空间音频的重要播放入口。
  • .monoStereoAndMultichannel 对齐 iOS 15、iPadOS 15、macOS Monterey 和 tvOS 15 的默认方向:单声道、立体声和多声道源在条件允许时都可空间化。
  • WebKit 的 MSE(Media Source Extensions,媒体源扩展)路径在 2021 秋季系统有有限支持。
  • MSE 没有用于定制空间化体验的接口。
  • WebKit 可通过 Media Capabilities API 中 AudioConfiguration 字典检测空间音频支持。

核心启发

  1. 做什么:给影视 App 增加多声道 HLS 播放。 为什么值得做:session 说明,HLS 变体引用多声道音频 alternate 后,AVFoundation 可以默认启用空间音频。 怎么开始:先整理媒体库中的多声道音频资源,按 HLS Authoring Specification 输出变体播放列表,再用 AVPlayerItem 播放并按需要设置 allowedAudioSpatializationFormats

  2. 做什么:在播放页显示“当前是否可用空间音频”。 为什么值得做:用户的耳机、扬声器和控制中心偏好会影响实际体验,App 需要把状态反馈给用户。 怎么开始:读取 AVAudioSession.sharedInstance().currentRoute.outputs,检查每个 AVAudioSessionPortDescriptionisSpatialAudioEnabled

  3. 做什么:当用户切换耳机或修改空间音频偏好时更新播放 UI。 为什么值得做:session 建议观察 route change,并监听空间播放能力变化通知。 怎么开始:注册 AVAudioSession.spatialPlaybackCapabilitiesChangedNotification,用 AVAudioSessionSpatialAudioEnabledKey 读取新状态,同时在路由变化时重新检查 isSpatialAudioEnabled

  4. 做什么:给自定义播放器声明多声道能力。 为什么值得做:使用 AVSampleBufferAudioRenderer 的 App 需要主动告诉系统自己可以提供多声道内容。 怎么开始:在播放前调用 try AVAudioSession.sharedInstance().setSupportsMultichannelContent(true),并通过 supportsMultichannelContent 验证状态。

  5. 做什么:为音乐会、体育和剧情视频制作声场叙事版本。 为什么值得做:session 的 demo 展示了空间音频对风声、环境声、远处呼叫、飞行和电影场景的叙事价值。 怎么开始:先为关键片段制作多声道音频,保证立体声和多声道 rendition 音量一致,补充 DRC 和 dialnorm 元数据,再用 HLS 发布。

关联 Session

评论

GitHub Issues · utterances