空间音频

空间音频是一种沉浸式音频体验,可让用户全身心地沉浸其中,让内容听起来更加逼真。这种音频具有“空间化”的特点,可以通过头戴式耳机营造多扬声器效果(类似于环绕声设置)。

例如,在电影中,汽车的声音可能会从用户背后开始,向前移动,然后逐渐远去。在视频聊天中,可以使用语音 放在用户周围,方便识别说话者。

如果您的内容使用受支持的音频格式,则可以将空间音频添加到 从 Android 13(API 级别 33)开始的应用。

查询功能

使用 Spatializer 类执行以下操作: 查询设备的空间化功能和行为。首先,从 AudioManager 检索 Spatializer 的实例:

KotlinJava
val spatializer = audioManager.spatializer
Spatializer spatializer = AudioManager.getSpatializer();

获取 Spatializer 后,检查必须满足的四个条件 true,以便设备输出空间音频:

条件 检查
设备是否支持空间化? getImmersiveAudioLevel() 不是 SPATIALIZER_IMMERSIVE_LEVEL_NONE
空间化是否可用?
是否可用取决于与当前音频输出路由的兼容性。
isAvailable()为“true
是否启用空间化? isEnabled()为“true
可以对具有指定参数的音轨进行空间化吗? canBeSpatialized()true

例如,如果没有空间化,可能无法满足这些条件 或在音频输出设备上完全停用该功能。

头部跟踪

使用受支持的耳机时,平台可以调整音频的 基于用户头部位置进行空间化处理。检查头部跟踪器是否 可用于当前音频输出路由,调用 isHeadTrackerAvailable()

兼容的内容

Spatializer.canBeSpatialized() 指示是否可以使用 当前输出设备路由。此方法接受 AudioAttributesAudioFormat,下面将对这两者进行详细介绍。

AudioAttributes

一个 AudioAttributes 对象 用于描述特定 API 的用法 音频流(例如游戏音频标准媒体), 及其播放行为和内容类型

调用 canBeSpatialized() 时,请使用与 Player 设置相同的 AudioAttributes 实例。例如,如果您使用的是 Jetpack Media3 库,并且尚未自定义 AudioAttributes,请使用 AudioAttributes.DEFAULT

停用空间音频

如需指明内容已进行空间化处理,请调用 setIsContentSpatialized(true),以免音频重复处理。或者,您也可以调用 setSpatializationBehavior(AudioAttributes.SPATIALIZATION_BEHAVIOR_NEVER) 来调整空间化行为,以完全停用空间化。

AudioFormat

AudioFormat 对象用于详细说明音轨的格式和声道配置。

实例化要传入 canBeSpatialized()AudioFormat 时,请将编码设置为与解码器预期的输出格式相同。您还应设置 通道掩码 与内容的频道配置相匹配的频道。请参阅 默认空间化行为部分,获取相关指导 使用特定值

监听 Spatializer 的更改

如需监听 Spatializer 状态的变化,您可以使用 Spatializer.addOnSpatializerStateChangedListener() 添加监听器。同样,如需监听头部跟踪器可用性方面的变化,请调用 Spatializer.addOnHeadTrackerAvailableListener()

如果您想在播放过程中调整曲目选择,这会非常有用 使用监听器的回调例如,当用户将耳机连接或断开连接到设备时,onSpatializerAvailableChanged 回调会指示空间化效果是否可用于新的音频输出路由。此时,您可以考虑更新播放器的 跟踪选择逻辑以匹配设备的新功能。如需详细了解 ExoPlayer 的曲目选择行为,请参阅 ExoPlayer 和空间音频部分。

ExoPlayer 和空间音频

ExoPlayer 的最新版本让用户可以更轻松地采用空间音频。如果您使用独立的 ExoPlayer 库(软件包名称为 com.google.android.exoplayer2),版本 2.17 会将平台配置为输出空间化音频,版本 2.18 会引入音频声道数限制。如果您使用 Media3 库中的 ExoPlayer 模块(软件包名称 androidx.media3),版本:1.0.0-beta01 和更新的版本中包含相同的更新。

将 ExoPlayer 依赖项更新到最新版本后,您的应用 需要包含可以空间化的内容

音频声道数限制

当满足空间音频的全部四个条件时,ExoPlayer 会选择 多声道音轨。否则,ExoPlayer 会改为选择立体声音轨。 如果 Spatializer 属性发生变化,ExoPlayer 会触发新的曲目选择,以选择符合 属性请注意,这一新的曲目选择可能会导致 重新缓冲期。

如需停用声道数量限制,请设置音轨选择参数 如下所示:

KotlinJava
exoPlayer.trackSelectionParameters = DefaultTrackSelector.Parameters.Builder(context)
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()
exoPlayer.setTrackSelectionParameters(
  new DefaultTrackSelector.Parameters.Builder(context)
    .setConstrainAudioChannelCountToDeviceCapabilities(false)
    .build()
);

同样,您也可以更新现有轨道选择器的参数,以停用 声道数量限制如下:

KotlinJava
val trackSelector = DefaultTrackSelector(context)
...
trackSelector.parameters = trackSelector.buildUponParameters()
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
...
trackSelector.setParameters(
  trackSelector
    .buildUponParameters()
    .setConstrainAudioChannelCountToDeviceCapabilities(false)
    .build()
);

如果内容包含多个音频,则停用声道数限制 ExoPlayer 最初会选择通道数最多的曲目, 。例如,如果内容包含 多声道音轨和立体声音轨,并且设备支持 ExoPlayer 选择多声道曲目。请参阅 音轨选择:详细了解如何自定义此行为。

音轨选择

当 ExoPlayer 的音频通道数限制 行为已停用,ExoPlayer 不会自动选择音轨 匹配设备空间化程序的属性。不过,您可以通过在播放前或播放期间设置轨道选择参数来自定义 ExoPlayer 的轨道选择逻辑。默认情况下,ExoPlayer 选择音频 与初始轨道相同的 MIME 类型 (编码)、通道数和采样率。

更改轨道选择参数

如需更改 ExoPlayer 的轨道选择参数,请使用 Player.setTrackSelectionParameters()。同样,您也可以使用 Player.getTrackSelectionParameters() 获取 ExoPlayer 的当前参数。例如,如需在播放过程中选择立体声音轨,请使用以下代码:

KotlinJava
exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters
  .buildUpon()
  .setMaxAudioChannelCount(2)
  .build()
exoPlayer.setTrackSelectionParameters(
  exoPlayer.getTrackSelectionParameters()
    .buildUpon()
    .setMaxAudioChannelCount(2)
    .build()
);

请注意,如果在播放过程中更改曲目选择参数,可能会导致 播放中断。如需详细了解如何调整播放器的曲目选择参数,请参阅 ExoPlayer 文档的曲目选择部分。

默认空间化行为

Android 中的默认空间化行为包括以下行为 可由原始设备制造商 (OEM) 自定义:

  • 只有多频道内容属于空间化内容,不支持立体声内容。 如果您不使用 ExoPlayer,则可能需要根据多声道音频内容的格式,将音频解码器可输出的声道数量上限配置为一个较大的数字。这样可以确保音频解码器输出多声道 PCM,以便平台进行空间化处理。

    KotlinJava
    val mediaFormat = MediaFormat()
    mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99)
    MediaFormat mediaFormat = new MediaFormat();
    mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99);

    如需查看实际操作示例,请参阅 ExoPlayer 的 MediaCodecAudioRenderer.java。如需自行关闭空间化(无论 OEM 是否进行了自定义),请参阅停用空间音频

  • AudioAttributes:如果 usage 设为 USAGE_MEDIAUSAGE_GAME,则音频符合空间化条件。

  • AudioFormat:使用的通道掩码至少包含 AudioFormat.CHANNEL_OUT_QUAD (前左、前右、左后和后右) 符合空间化条件。在下面的示例中,我们使用 AudioFormat.CHANNEL_OUT_5POINT1 表示 5.1 音轨。对于立体声音轨,请使用 AudioFormat.CHANNEL_OUT_STEREO

    如果您使用的是 Media3,则可以使用 Util.getAudioTrackChannelConfig(int channelCount) 将声道数转换为声道掩码。

    此外,将编码设置为 AudioFormat.ENCODING_PCM_16BIT (如果您已将解码器配置为输出多声道 PCM)。

    KotlinJava
    val audioFormat = AudioFormat.Builder()
      .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
      .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
      .build()
    AudioFormat audioFormat = new AudioFormat.Builder()
      .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
      .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
      .build();

测试空间音频

确保测试设备上已启用空间音频功能:

  • 如果您使用的是有线耳机,请依次前往系统设置 >声音和振动 >空间 音频
  • 对于无线耳机,请转到系统设置 >已连接的设备 >齿轮图标 无线设备 >空间音频

要检查当前路由是否可使用空间音频,请运行 adb shell dumpsys audio 命令。播放时,您应该会在输出中看到以下参数:

Spatial audio:
mHasSpatializerEffect:true (effect present)
isSpatializerEnabled:true (routing dependent)