Android 的 OpenSL ES

警告:OpenSL ES 已 **弃用**。开发者应使用开源 Oboe 库,该库可在 GitHub 上获取。Oboe 是一个 C++ 包装器,提供了一个与 AAudio 非常相似的 API。当 AAudio 可用时,Oboe 会调用 AAudio,如果 AAudio 不可用,则会回退到 OpenSL ES。

此页面详细介绍了 NDK 实现的 OpenSL ES™ 与 OpenSL ES 1.0.1 参考规范的不同之处。使用规范中的示例代码时,可能需要对其进行修改以使其在 Android 上运行。

除非另有说明,否则所有功能均在 Android 2.3(API 级别 9)及更高版本中可用。某些功能仅适用于 Android 4.0(API 级别 14);这些功能已注明。

注意: Android 兼容性定义文档 (CDD) 列出了兼容 Android 设备的硬件和软件要求。有关整体兼容性计划的更多信息,请参阅 Android 兼容性;有关实际 CDD 文档,请参阅 CDD

OpenSL ES 提供了一个 C 语言接口,也可以使用 C++ 访问。它公开的功能类似于以下 Android Java API 的音频部分

与所有 Android 原生开发套件 (NDK) 一样,Android 的 OpenSL ES 的主要目的是促进共享库的实现,以便使用 Java 本地接口 (JNI ) 调用这些库。NDK 不适用于编写纯 C/C++ 应用。但是,OpenSL ES 是一个功能齐全的 API,我们预计您应该能够仅使用此 API 完成大部分音频需求,而无需向上调用在 Android 运行时中运行的代码。

注意: 虽然基于 OpenSL ES,但 Android 原生音频(高性能音频)API 并非 OpenSL ES 1.0.1 的任何配置文件(游戏、音乐或电话)的符合实现。这是因为 Android 未实现任何一个配置文件所需的所有功能。在 Android 的行为与规范不同的任何已知情况下,都会在 Android 扩展 页面中进行描述。

从参考规范继承的功能

Android NDK 中 OpenSL ES 的实现继承了参考规范中的大部分功能集,但存在一些限制。

全局入口点

适用于 Android 的 OpenSL ES 支持 Android 规范中的所有全局入口点。这些入口点包括

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

对象和接口

下表显示了 Android NDK 中 OpenSL ES 实现支持的对象和接口。如果单元格中显示 ,则表示此实现中提供了该功能。

Android NDK 对对象和接口的支持。

功能 音频播放器 音频录制器 引擎 输出混音器
低音增强
缓冲区队列
缓冲区队列数据定位器 是:源
动态接口管理
效果发送
引擎
环境混响
均衡器
I/O 设备数据定位器 是:源
元数据提取 是:解码为 PCM
静音独奏
对象
输出混音器定位器 是:接收器
播放
播放速率
预取状态
预设混响
录制
查找
URI 数据定位器 是:源
虚拟化器
音量

下一节将解释其中一些功能的限制。

限制

表 1 中的功能适用某些限制。这些限制表示与参考规范的差异。本节的其余部分提供了有关这些差异的信息。

动态接口管理

适用于 Android 的 OpenSL ES 不支持 RemoveInterfaceResumeInterface

效果组合:环境混响和预设混响

同一个输出混音器上不能同时使用环境混响和预设混响。

如果平台估计 CPU 负载过高,则可能会忽略效果请求。

效果发送

SetSendLevel() 支持每个音频播放器一个发送级别。

环境混响

环境混响不支持 SLEnvironmentalReverbSettings 结构的 reflectionsDelayreflectionsLevelreverbDelay 字段。

MIME 数据格式

仅当使用 URI 数据定位器且仅用于音频播放器时,才能使用 MIME 数据格式。无法将此数据格式用于音频录制器。

Android 中 OpenSL ES 的实现要求您将 mimeType 初始化为 NULL 或有效的 UTF-8 字符串。您还必须将 containerType 初始化为有效值。在没有其他考虑因素的情况下(例如,可移植性到其他实现或应用程序无法通过标头识别的内容格式),建议您将 mimeType 设置为 NULL,并将 containerType 设置为 SL_CONTAINERTYPE_UNSPECIFIED

适用于 Android 的 OpenSL ES 支持以下音频格式,只要 Android 平台也支持这些格式即可

  • WAV PCM。
  • WAV alaw。
  • WAV ulaw。
  • MP3 Ogg Vorbis。
  • AAC LC。
  • HE-AACv1 (AAC+)。
  • HE-AACv2 (增强型 AAC+)。
  • AMR。
  • FLAC。

注意: 有关 Android 支持的音频格式列表,请参阅 支持的媒体格式

以下限制适用于在此 OpenSL ES 实现中处理这些格式和其他格式。

  • AAC 格式必须位于 MP4 或 ADTS 容器中。
  • 适用于 Android 的 OpenSL ES 不支持 MIDI
  • WMA 不属于 AOSP,我们尚未验证其与适用于 Android 的 OpenSL ES 的兼容性。
  • Android NDK 中 OpenSL ES 的实现不支持直接播放 DRM 或加密内容。要播放受保护的音频内容,您必须在播放前在应用程序中对其进行解密,并由您的应用程序执行任何 DRM 限制。

适用于 Android 的 OpenSL ES 不支持以下用于操作对象的方法

  • Resume()
  • RegisterCallback()
  • AbortAsyncOperation()
  • SetPriority()
  • GetPriority()
  • SetLossOfControlInterfaces()

PCM 数据格式

PCM 是您可以与缓冲区队列一起使用的唯一数据格式。支持的 PCM 播放配置具有以下特征

  • 8 位无符号或 16 位有符号。
  • 单声道或立体声。
  • 小端字节序。
  • 采样率为
    • 8,000 赫兹。
    • 11,025 赫兹。
    • 12,000 赫兹。
    • 16,000 赫兹。
    • 22,050 赫兹。
    • 24,000 赫兹。
    • 32,000 赫兹。
    • 44,100 赫兹。
    • 48,000 赫兹。

适用于 Android 的 OpenSL ES 支持的录制配置取决于设备;通常,无论设备如何,都提供 16,000 赫兹单声道/16 位有符号。

samplesPerSec 字段的值以毫赫兹为单位,尽管名称具有误导性。为避免意外使用错误的值,建议您使用为此目的定义的符号常量之一(例如 SL_SAMPLINGRATE_44_1)来初始化此字段。

Android 5.0(API 级别 21)及更高版本支持 浮点数据

播放速率

OpenSL ES 播放速率指示对象呈现数据的速度,以千分之一的正常速度或千分率表示。例如,1,000 千分率的播放速率为 1,000/1,000,即正常速度。速率范围是一个封闭区间,表示可能的播放速率范围。

对播放速率范围和其他功能的支持可能因平台版本和实现而异。您的应用程序可以通过使用 PlaybackRate::GetRateRange()PlaybackRate::GetCapabilitiesOfRate() 查询设备来在运行时确定这些功能。

设备通常支持 PCM 格式数据源的相同速率范围,以及其他格式的 1000 千分率至 1000 千分率的单位速率范围;也就是说,单位速率范围实际上是单个值。

录制

适用于 Android 的 OpenSL ES 不支持 SL_RECORDEVENT_HEADATLIMITSL_RECORDEVENT_HEADMOVING 事件。

查找

SetLoop() 方法启用整个文件的循环。要启用循环,请将 startPos 参数设置为 0,并将 endPos 参数设置为 SL_TIME_UNKNOWN

缓冲区队列数据定位器

具有缓冲区队列数据定位器的音频播放器或录制器仅支持 PCM 数据格式。

I/O 设备数据定位器

适用于 Android 的 OpenSL ES 仅在您将定位器指定为 Engine::CreateAudioRecorder() 的数据源时,才支持使用 I/O 设备数据定位器。使用以下代码片段中包含的值初始化设备数据定位器

SLDataLocator_IODevice loc_dev =
  {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
  SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};

URI 数据定位器

适用于 Android 的 OpenSL ES 只能将 URI 数据定位器与 MIME 数据格式一起使用,并且只能用于音频播放器。您无法将 URI 数据定位器用于音频录制器。URI 只能使用 http:file: 方案。不允许使用其他方案,例如 https:ftp:content:

我们尚未验证 Android 平台上音频是否支持 rtsp:

数据结构

Android 支持这些 OpenSL ES 1.0.1 数据结构

  • SLDataFormat_MIME
  • SLDataFormat_PCM
  • SLDataLocator_BufferQueue
  • SLDataLocator_IODevice
  • SLDataLocator_OutputMix
  • SLDataLocator_URI
  • SLDataSink
  • SLDataSource
  • SLEngineOption
  • SLEnvironmentalReverbSettings
  • SLInterfaceID

平台配置

适用于 Android 的 OpenSL ES 专为多线程应用程序设计,并且是线程安全的。它支持每个应用程序一个引擎,每个引擎最多支持 32 个对象。可用的设备内存和 CPU 可能会进一步限制可用的对象数量。

识别这些引擎选项,但 slCreateEngine 会忽略它们

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

可以在同一个应用程序中同时使用 OpenMAX AL 和 OpenSL ES。在这种情况下,内部只有一个共享的引擎对象,并且 32 个对象的限制在 OpenMAX AL 和 OpenSL ES 之间共享。应用程序应创建这两个引擎,使用这两个引擎,最后销毁这两个引擎。实现维护共享引擎上的引用计数,以便在第二次销毁操作期间正确销毁它。

编程注意事项

OpenSL ES 编程注意事项 提供补充信息,以确保 OpenSL ES 的正确实现。

注意: 为方便起见,我们在 NDK 中包含了 OpenSL ES 1.0.1 规范的副本,位于 docs/opensles/OpenSL_ES_Specification_1.0.1.pdf

平台问题

本节描述了支持这些 API 的初始平台版本中的已知问题。

动态接口管理

DynamicInterfaceManagement::AddInterface 不起作用。而是像环境混响的示例代码中所示,在传递给 Create() 的数组中指定接口。

OpenSL ES 未来版本的计划

Android 高性能音频 API 基于 Khronos Group OpenSL ES 1.0.1。Khronos 发布了修订版 1.1 的标准。修订版包含新功能、说明、排版错误的更正以及一些不兼容性。大多数预期不兼容性相对较小,或者位于 Android 不支持的 OpenSL ES 领域。

使用此版本开发的应用程序应在 Android 平台的未来版本上运行,前提是您遵循下面 二进制兼容性计划 部分中概述的准则。

注意: 未来源代码兼容性不是目标。也就是说,如果您升级到较新版本的 NDK,则可能需要修改应用程序源代码以符合新的 API。我们预计大多数此类更改都将很小;请参阅下面的详细信息。

二进制兼容性计划

我们建议您的应用程序遵循以下指南,以提高未来的二进制兼容性。

  • 仅使用 OpenSL ES 1.0.1 中 Android 支持功能的已记录子集。
  • 不要依赖于特定操作失败的结果代码;做好处理不同结果代码的准备。
  • 应用程序回调处理程序通常在受限的上下文中运行。它们应该编写为快速执行其工作,并在尽可能短的时间内返回。不要在回调处理程序中运行复杂的操作。例如,在缓冲区队列完成回调中,您可以将另一个缓冲区入队,但不要创建音频播放器。
  • 回调处理程序应做好准备,以或多或少频繁地被调用,接收其他事件类型,并忽略它们无法识别的事件类型。配置了由启用事件类型组成的事件掩码的回调应准备好被调用,并同时设置多个事件类型位。使用 "&" 测试每个事件位,而不是使用 switch case。
  • 使用预取状态和回调作为进度的一般指示,但不要依赖于特定的硬编码填充级别或回调序列。预取状态填充级别的含义以及在预取期间检测到的错误的行为可能会发生变化。

注意: 有关更多详细信息,请参见下面的 缓冲区队列行为 部分。

计划源代码兼容性

如前所述,Khronos Group 的下一版本 OpenSL ES 中预计会出现源代码不兼容性。可能发生变化的领域包括

  • 缓冲区队列接口预计会有重大变化,尤其是在 BufferQueue::EnqueueslBufferQueueCallback 的参数列表以及字段 SLBufferQueueState.playIndex 的名称方面。我们建议您的应用程序代码使用 Android 简单缓冲区队列。在 NDK 提供的示例代码中,由于此原因,我们使用了 Android 简单缓冲区队列进行播放。(我们还使用 Android 简单缓冲区队列进行录制和解码到 PCM,但这是因为标准 OpenSL ES 1.0.1 不支持录制或解码到缓冲区队列数据接收器。)
  • 将向通过引用传递的输入参数以及用作输入值的 SLchar * 结构字段添加 const。这不需要对您的代码进行任何更改。
  • 将用无符号类型替换一些当前为有符号的参数。您可能需要将参数类型从 SLint32 更改为 SLuint32 或类似类型,或添加强制转换。
  • Equalizer::GetPresetName 将字符串复制到应用程序内存,而不是返回指向实现内存的指针。这将是一个重大的变化,因此我们建议您避免调用此方法,或隔离您对它的使用。
  • 结构类型中将有其他字段。对于输出参数,可以忽略这些新字段,但对于输入参数,需要初始化新字段。幸运的是,所有这些字段都预计位于 Android 不支持的区域。
  • 接口 GUID 将发生变化。通过符号名称而不是 GUID 来引用接口,以避免依赖关系。
  • SLchar 将从 unsigned char 更改为 char。这主要影响 URI 数据定位器和 MIME 数据格式。
  • SLDataFormat_MIME.mimeType 将重命名为 pMimeTypeSLDataLocator_URI.URI 将重命名为 pURI。我们建议您使用花括号括起来的逗号分隔的值列表来初始化 SLDataFormat_MIMESLDataLocator_URI 数据结构,而不是按字段名称来初始化,以隔离您的代码免受此更改的影响。此技术在示例代码中使用。
  • SL_DATAFORMAT_PCM 不允许应用程序指定数据的表示形式为有符号整数、无符号整数或浮点数。Android 实现假设 8 位数据为无符号整数,16 位数据为有符号整数。此外,字段 samplesPerSec 是一个误称,因为实际单位是毫赫兹。这些问题预计将在下一个 OpenSL ES 版本中得到解决,该版本将引入一种新的扩展 PCM 数据格式,允许应用程序显式指定表示形式并更正字段名称。由于这将是一种新的数据格式,并且当前的 PCM 数据格式仍然可用(尽管已弃用),因此它不需要对您的代码进行任何立即更改。