警告:OpenSL ES 已 **弃用**。开发者应使用可在 GitHub 上获取的开源 Oboe 库。Oboe 是一个 C++ 包装器,提供与 AAudio 非常相似的 API。当 AAudio 可用时,Oboe 会调用 AAudio,如果 AAudio 不可用,则会回退到 OpenSL ES。
适用于 Android 的 OpenSL ES 扩展了参考 OpenSL ES 规范,使其与 Android 兼容,并利用 Android 平台的强大功能和灵活性。
Android 扩展的 API 定义位于 OpenSLES_Android.h
及其包含的头文件中。有关这些扩展的详细信息,请查阅 OpenSLES_Android.h
。此文件位于您的安装根目录下的 sysroot/usr/include/SLES
目录中。除非另有说明,否则所有接口都是显式的。
这些扩展会限制您的应用移植到其他 OpenSL ES 实现的可能性,因为它们是 Android 专用的。您可以通过避免使用扩展或使用 #ifdef
在编译时将其排除来缓解此问题。
下表显示了 Android OpenSL ES 支持的每个对象类型的 Android 专用接口和数据定位器。单元格中的 是 值表示每个对象类型可用的接口和数据定位器。
功能 | 音频播放器 | 音频录制器 | 引擎 | 输出混合器 |
---|---|---|---|---|
Android 缓冲区队列 | 是:源(解码) | 否 | 否 | 否 |
Android 配置 | 是 | 是 | 否 | 否 |
Android 效果 | 是 | 否 | 否 | 是 |
Android 效果功能 | 否 | 否 | 是 | 否 |
Android 效果发送 | 是 | 否 | 否 | 否 |
Android 简单缓冲区队列 | 是:源(播放)或接收器(解码) | 是 | 否 | 否 |
Android 缓冲区队列数据定位器 | 是:源(解码) | 否 | 否 | 否 |
Android 文件描述符数据定位器 | 是:源 | 否 | 否 | 否 |
Android 简单缓冲区队列数据定位器 | 是:源(播放)或接收器(解码) | 是:接收器 | 否 | 否 |
Android 配置接口
Android 配置接口提供了一种为对象设置平台特定参数的方法。此接口与其他 OpenSL ES 1.0.1 接口的不同之处在于,您的应用可以在实例化相应对象之前使用它;因此,您可以在实例化对象之前对其进行配置。 OpenSLES_AndroidConfiguration.h
头文件位于 /sysroot/usr/include/SLES
,其中记录了以下可用的配置键和值
- 音频播放器的流类型(默认值为
SL_ANDROID_STREAM_MEDIA
)。 - 音频录制器的录制配置文件(默认值为
SL_ANDROID_RECORDING_PRESET_GENERIC
)。
以下代码片段演示了如何在音频播放器上设置 Android 音频流类型的示例
// CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION // in the required interface ID array. Do not realize player yet. // ... SLAndroidConfigurationItf playerConfig; result = (*playerObject)->GetInterface(playerObject, SL_IID_ANDROIDCONFIGURATION, &playerConfig); assert(SL_RESULT_SUCCESS == result); SLint32 streamType = SL_ANDROID_STREAM_ALARM; result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); assert(SL_RESULT_SUCCESS == result); // ... // Now realize the player here.
您可以使用类似的代码来配置音频录制器的预设
// ... obtain the configuration interface as the first four lines above, then: SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; result = (*playerConfig)->SetConfiguration(playerConfig, RECORDING_PRESET, &presetValue, sizeof(SLuint32));
Android 效果接口
Android 的效果、效果发送和效果功能接口为应用程序提供了一种通用机制来查询和使用设备特定的音频效果。设备制造商应记录他们提供的任何可用的设备特定音频效果。
可移植应用程序应使用 OpenSL ES 1.0.1 API 进行音频效果处理,而不是使用 Android 效果扩展。
Android 文件描述符数据定位器
Android 文件描述符数据定位器允许您将音频播放器的源指定为具有读取访问权限的打开文件描述符。数据格式必须为 MIME。
此扩展与原生资产管理器结合使用特别有用,因为应用程序通过文件描述符从 APK 读取资产。
Android 简单缓冲区队列数据定位器和接口
在 OpenSL ES 1.0.1 参考规范中,缓冲区队列只能用于音频播放器,并且与 PCM 及其他数据格式兼容。Android 简单缓冲区队列数据定位器和接口规范与参考规范相同,但有两个例外
- 您可以将 Android 简单缓冲区队列与音频录制器和音频播放器一起使用。
- 您只能将 PCM 数据格式与这些队列一起使用。
对于录制,您的应用应将空缓冲区入队。当注册的回调发送系统已完成写入数据到缓冲区的通知时,应用可以从该缓冲区读取数据。
回放的工作方式相同。但是,为了将来源代码的兼容性,我们建议应用程序使用 Android 简单缓冲区队列,而不是 OpenSL ES 1.0.1 缓冲区队列。
缓冲区队列行为
Android 实现不包含参考规范中关于当回放进入 SL_PLAYSTATE_STOPPED
状态时播放光标必须返回到当前播放缓冲区开头的要求。此实现可以符合该行为,也可以保持播放光标的位置不变。因此,您的应用不能假设两种行为都会发生。因此,您应该在过渡到 SL_PLAYSTATE_STOPPED
后显式调用 BufferQueue::Clear()
方法。这样做会将缓冲区队列设置为已知状态。
同样,也没有规范规定缓冲区队列回调的触发器必须是过渡到 SL_PLAYSTATE_STOPPED
还是执行 BufferQueue::Clear()
。因此,我们建议您不要创建对其中一个的依赖关系;相反,您的应用应该能够处理两者。
对象创建时的动态接口
为了方便起见,OpenSL ES 1.0.1 的 Android 实现允许您的应用在实例化对象时指定动态接口。这是一种替代使用 DynamicInterfaceManagement::AddInterface()
在实例化后添加这些接口的方法。
扩展的报告
有三种方法可以查询平台是否支持 Android 扩展。这些方法是
Engine::QueryNumSupportedExtensions()
Engine::QuerySupportedExtension()
Engine::IsExtensionSupported()
任何这些方法都返回 ANDROID_SDK_LEVEL_<API-level>
,其中 API-level
是平台 API 级别;例如,ANDROID_SDK_LEVEL_23
。平台 API 级别为 9 或更高意味着平台支持扩展。
将音频解码为 PCM
本节介绍 OpenSL ES 1.0.1 的一个已弃用的 Android 特定扩展,用于在不立即播放的情况下将编码流解码为 PCM。下表提供了使用此扩展和替代方案的建议。
API 级别 | 替代方案 |
---|---|
15 及以下 | 具有合适许可证的开源编解码器 |
16 到 20 | MediaCodec 类或具有合适许可证的开源编解码器 |
21 及以上 | NDK MediaCodec(位于 <media/NdkMedia*.h> 头文件中)、MediaCodec 类或具有合适许可证的开源编解码器 |
注意: 目前没有关于 MediaCodec
API 的 NDK 版本的文档。但是,您可以参考 native-codec 示例代码以了解示例。
标准音频播放器回放至音频设备,指定输出混音作为数据接收器。Android 扩展的不同之处在于,如果应用程序将数据源指定为 URI 或使用 MIME 数据格式描述的 Android 文件描述符数据定位器,则音频播放器将充当解码器。在这种情况下,数据接收器是使用 PCM 数据格式的 Android 简单缓冲区队列数据定位器。
此功能主要用于游戏在切换到新游戏关卡时预加载其音频资产,这类似于 SoundPool
类提供的功能。
应用程序应首先在 Android 简单缓冲区队列中入队一组空缓冲区。之后,应用使用 PCM 数据填充缓冲区。每个缓冲区填充后,Android 简单缓冲区队列回调就会触发。回调处理程序处理 PCM 数据,重新入队现在为空的缓冲区,然后返回。应用程序负责跟踪解码后的缓冲区;回调参数列表不包含足够的信息来指示包含数据的缓冲区或应入队的下一个缓冲区。
数据源通过在流的末尾传递 SL_PLAYEVENT_HEADATEND
事件来隐式报告流的结束 (EOS)。在应用程序解码了接收到的所有数据后,它不会再对 Android 简单缓冲区队列回调进行任何调用。
接收器的 PCM 数据格式通常在采样率、通道数和位深度方面与编码数据源匹配。但是,您可以解码为不同的采样率、通道数或位深度。有关检测实际 PCM 格式的规定的信息,请参阅 通过元数据确定解码后的 PCM 数据的格式。
Android 的 OpenSL ES PCM 解码功能支持暂停和初始查找;它不支持音量控制、效果、循环或播放速率。
根据平台实现,解码可能需要无法空闲的资源。因此,我们建议您确保提供足够数量的空 PCM 缓冲区;否则,解码器会发生饥饿。例如,如果您的应用从 Android 简单缓冲区队列回调返回而没有入队另一个空缓冲区,则可能会发生这种情况。解码器饥饿的结果未指定,但可能包括:丢弃解码后的 PCM 数据、暂停解码过程或完全终止解码器。
注意: 对于在 Android 4.x(API 级别 16-20)上运行的应用,如果要将编码流解码为 PCM 但不立即回放,我们建议使用 MediaCodec
类。对于在 Android 5.0(API 级别 21)或更高版本上运行的新应用程序,我们建议使用 NDK 等效项 <NdkMedia*.h>
。这些头文件位于安装根目录下的 media/
目录中。
将流式 ADTS AAC 解码为 PCM
如果数据源是使用 MIME 数据格式的 Android 缓冲区队列数据定位器,并且数据接收器是使用 PCM 数据格式的 Android 简单缓冲区队列数据定位器,则音频播放器将充当流式解码器。按如下方式配置 MIME 数据格式
- 容器:
SL_CONTAINERTYPE_RAW
- MIME 类型字符串:
SL_ANDROID_MIME_AACADTS
此功能主要用于处理 AAC 音频但需要在回放前执行自定义音频处理的流媒体应用程序。大多数需要将音频解码为 PCM 的应用程序应使用 将音频解码为 PCM 中描述的方法,因为该方法更简单且处理更多音频格式。此处描述的技术是一种更专业的方案,仅在以下两个条件都适用时才使用
- 压缩音频源是包含在 ADTS 标头中的 AAC 帧的流。
- 应用程序管理此流。数据不位于其标识符为 URI 的网络资源中,也不位于其标识符为文件描述符的本地文件中。
应用程序应首先在 Android 缓冲区队列中入队一组已填充的缓冲区。每个缓冲区包含一个或多个完整的 ADTS AAC 帧。每个缓冲区清空后,Android 缓冲区队列回调就会触发。回调处理程序应重新填充和重新入队缓冲区,然后返回。应用程序无需跟踪编码后的缓冲区;回调参数列表包含足够的信息来指示应入队的下一个缓冲区。流的结束由入队 EOS 项目显式标记。在 EOS 之后,不允许再入队。
我们建议您确保提供完整的 ADTS AAC 缓冲区,以避免解码器饥饿。例如,如果您的应用从 Android 缓冲区队列回调返回而没有入队另一个完整缓冲区,则可能会发生这种情况。解码器饥饿的结果未指定。
除了数据源外,流式解码方法与 将音频解码为 PCM 中描述的方法相同。
尽管名称相似,但 Android 缓冲区队列不同于 Android 简单缓冲区队列。流式解码器使用两种缓冲区队列:用于 ADTS AAC 数据源的 Android 缓冲区队列,以及用于 PCM 数据接收器的 Android 简单缓冲区队列。有关 Android 简单缓冲区队列 API 的更多信息,请参阅 Android 简单缓冲区队列数据定位器和接口。有关 Android 缓冲区队列 API 的更多信息,请参阅安装根目录下 docs/Additional_library_docs/openmaxal/
目录中的 index.html
文件。
通过元数据确定解码后的 PCM 数据的格式
SLMetadataExtractionItf
接口是参考规范的一部分。但是,指示解码 PCM 数据实际格式的元数据键是 Android 特定的。 OpenSLES_AndroidMetadata.h
头文件定义了这些元数据键。此头文件位于您的安装根目录下,在 /sysroot/usr/include/SLES
目录中。
元数据键索引在 Object::Realize()
方法执行完成后立即可用。但是,关联的值要等到应用程序解码第一个编码数据后才能使用。一个好的做法是在调用 Object::Realize
方法后,在主线程中查询键索引,并在首次调用 Android 简单缓冲区队列回调处理程序时读取 PCM 格式元数据值。请参阅 NDK 软件包中的示例代码,了解使用此接口的示例。
元数据键名称是稳定的,但键索引未记录,并且可能会更改。应用程序不应假设索引在不同的执行运行中是持久性的,也不应假设多个对象实例在同一运行中共享索引。
浮点数据
在 Android 5.0(API 级别 21)及更高版本上运行的应用程序可以以单精度浮点格式向 AudioPlayer 提供数据。
在以下示例代码中,Engine::CreateAudioPlayer()
方法创建一个使用浮点数据的音频播放器。
#include <SLES/OpenSLES_Android.h> ... SLAndroidDataFormat_PCM_EX pcm; pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; pcm.numChannels = 2; pcm.sampleRate = SL_SAMPLINGRATE_44_1; pcm.bitsPerSample = 32; pcm.containerSize = 32; pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; ... SLDataSource audiosrc; audiosrc.pLocator = ... audiosrc.pFormat = &pcm;