面向 Android 的 OpenSL ES

本页面详细介绍了 OpenSL ES™ 的 NDK 实现与 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 行为与规范不同的任何已知情况进行了说明。

从参考规范继承的功能

OpenSL ES 的 Android NDK 实现从参考规范中集成了大量功能集,但具有一定限制。

全局入口点

面向 Android 的 OpenSL ES 支持 Android 规范中的所有全局入口点。这些入口点包括:

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

对象与接口

下表显示了 OpenSL ES 的 Android NDK 实现支持的对象与接口。 如果单元格中为,则说明相应功能在此实现中可用。

Android NDK 的对象与接口支持。

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

下一部分将介绍其中一些功能的限制。

限制

表 1 中的功能可能存在某些限制。这些限制表示与参考规范的不同。 本部分的剩余内容将提供有关这些不同的信息。

动态接口管理

面向 Android 的 OpenSL ES 不支持 RemoveInterfaceResumeInterface

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

无法在同一输出混合上同时拥有环境混响和预设混响。

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

效果送出

SetSendLevel() 支持每个音频播放器拥有一个送出水平。

环境混响

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

MIME 数据格式

您仅可以将 MIME 数据格式与 URI 数据定位器结合使用,并且只能用于音频播放器。 无法将此数据格式用于音频录制器。

OpenSL ES 的 Android 实现需要您将 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 的兼容性。
  • OpenSL ES 的 Android NDK 实现不支持直接回放 DRM 或加密内容。 要回放受保护的音频内容,您必须在应用中将其解密,然后才能使用强制实施了任何 DRM 限制的应用播放。

对象相关的方法

面向 Android 的 OpenSL ES 不支持以下对象操作方法:

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

PCM 数据格式

PCM 是唯一一种您可以用于缓冲区队列的数据格式。支持的 PCM 回放配置具有以下特性:

  • 8 位无符号或 16 位有符号。
  • 单声道或立体声。
  • 小字节序字节排序。
  • 下列采样率:
    • 8,000 Hz。
    • 11,025 Hz。
    • 12,000 Hz。
    • 16,000 Hz。
    • 22,050 Hz。
    • 24,000 Hz。
    • 32,000 Hz。
    • 44,100 Hz。
    • 48,000 Hz。

面向 Android 的 OpenSL ES 支持的录制配置取决于设备;通常情况下,无论什么设备都支持 16,000 Hz 单声道/16 位有符号配置。

samplesPerSec 字段值的单位为 milliHz,单位名称容易引起误导。 为了避免意外使用错误的值,我们建议您使用为此目的定义的一个符号常量(例如 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 设备数据定位器

如果您已将定位器指定为 Engine::CreateAudioRecorder() 的数据源,则面向 Android 的 OpenSL ES 将仅支持使用 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 的补充信息。

注:为方便起见,我们已在 docs/opensles/OpenSL_ES_Specification_1.0.1.pdf 中包含了一个带 NDK 的 OpenSL ES 1.0.1 规范副本。

平台问题

本部分将说明支持这些 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::Enqueue 的区域、slBufferQueueCallback 的参数列表和字段 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 也是一个不恰当的名称,因为实际单位为 milliHz。 这些问题预计将在下一个版本的 OpenSL ES 中得到解决,新版本将引入一个新的扩展 PCM 数据格式,此数据格式允许应用明确指定表示和校正字段名称。 由于这是一种新数据格式,当前的 PCM 数据格式仍会继续使用(尽管已被弃用),因此您不需要立即对代码进行更改。