空间音频

空间音频可提供身临其境的音频体验,让用户能够身临其境 动作的中心,让内容听起来更逼真。声音是 "空间化"创建类似于环绕声的多扬声器效果 而是通过头戴式耳机进行设置

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

如果您的内容使用受支持的音频格式,则可以将空间音频添加到 应用。

查询功能

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

Kotlin

val spatializer = audioManager.spatializer

Java

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 对象用于描述 有关音轨格式和声道配置的详细信息。

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

监听 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 会触发新的曲目选择,以选择符合 属性请注意,这一新的曲目选择可能会导致 重新缓冲期。

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

Kotlin

exoPlayer.trackSelectionParameters = DefaultTrackSelector.Parameters.Builder(context)
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()

Java

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

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

Kotlin

val trackSelector = DefaultTrackSelector(context)
...
trackSelector.parameters = trackSelector.buildUponParameters()
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()

Java

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

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

音轨选择

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

更改轨道选择参数

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

Kotlin

exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters
  .buildUpon()
  .setMaxAudioChannelCount(2)
  .build()

Java

exoPlayer.setTrackSelectionParameters(
  exoPlayer.getTrackSelectionParameters()
    .buildUpon()
    .setMaxAudioChannelCount(2)
    .build()
);

请注意,如果在播放过程中更改曲目选择参数,可能会导致 播放中断。关于调整播放器轨道的详细信息 选择参数中 轨道选择 部分。

默认空间化行为

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

  • 只有多频道内容是空间化内容,没有立体声内容。 如果您不使用 ExoPlayer,请根据多频道采用的格式 音频内容,您可能需要配置声道数量上限 可以由音频解码器输出到大量数字。这样可以确保 音频解码器会输出多声道 PCM,以供平台实现空间化。

    Kotlin

    val mediaFormat = MediaFormat()
    mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99)
    

    Java

    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 。对于立体声音轨,请使用 AudioFormat.CHANNEL_OUT_STEREO

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

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

    Kotlin

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

    Java

    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)