Media3 MediaSession
  • Media3 ExoPlayer
  • Media3 Transformer
  • Media3 界面
  • Jetpack MediaRouter
  • 平台 API 和行为
  • 旧版媒体 API
  • 添加相机功能 扩大应用的覆盖范围 Android 设备类型
  • Google Assistant
  • 正在投放设备上播放
  • 提升媒体应用体验 针对高画质进行优化

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

    例如,在电影中,汽车的声音可能会从用户背后开始,向前移动,然后逐渐远去。在视频聊天中,系统可以将声音分离并放置在用户周围,以便更轻松地识别发言者。

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

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

    Kotlin

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

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

    设备是否支持空间化? getImmersiveAudioLevel() 不是 SPATIALIZER_IMMERSIVE_LEVEL_NONE 是否支持空间化?
    是否可用取决于与当前音频输出路由的兼容性。 isAvailable() true 是否 启用了 空间化? isEnabled() true 指定参数 的音轨能否进行空间化处理? canBeSpatialized() true

    这些条件可能不满足,例如,如果当前音轨不支持空间化或音频输出设备上完全停用了空间化。

    在支持的头戴式耳机上,平台可以根据用户的头部位置调整音频的空间化效果。如需检查头部追踪器是否适用于当前的音频输出路由,请调用 isHeadTrackerAvailable()

    兼容的内容

    Spatializer.canBeSpatialized() 指示具有给定属性的音频是否可以使用当前输出设备路由进行空间化处理。此方法接受 AudioAttributes AudioFormat ,下面将详细介绍这两者。

    AudioAttributes

    AudioAttributes 对象用于描述音频串流的 用途 (例如 游戏音频 标准媒体 ),以及其播放行为和 内容类型

    调用 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 将触发新的轨道选择,以选择与当前属性匹配的音轨。请注意,选择新轨道可能会导致短暂的重新缓冲期。

    如需停用音频声道数限制,请在播放器上设置曲目选择参数,如下所示:

    Kotlin

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

    同样,您可以更新现有轨道选择器的参数,以停用音频声道数约束条件,如下所示:

    Kotlin

    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 的当前参数。例如,如需在播放过程中选择立体声音轨,请执行以下操作:

    Kotlin

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

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

    默认的空间化行为

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

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

    Kotlin

    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) 将声道计数转换为声道掩码。

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

    Kotlin

    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)
          [[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["没有我需要的信息","missingTheInformationINeed","thumb-down"],["太复杂/步骤太多","tooComplicatedTooManySteps","thumb-down"],["内容需要更新","outOfDate","thumb-down"],["翻译问题","translationIssue","thumb-down"],["示例/代码问题","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-06-11。"],[],[]]
    
  •