本文将详细说明NDK中OpenSL ES™的实现与OpenSL ES 1.0.1参考规范的不同。在使用规范中的示例代码时,可能需要修改它以在Android上工作。
除非另有说明,所有功能都可以在Android 2.3 (API level 9)及以上版本中使用。有些功能只适用于Android 4.0 (API level 14);这些会指出。
注意:Android兼容定义文档(CDD)列举了兼容Android设备的硬件和软件需求。有关总体兼容性程序的更多信息,请参阅Android兼容性,以及有关实际CDD文档的CDD。
OpenSL ES提供了一个C语言接口,也可以使用c++进行访问。
与所有Android Native Development Kit (NDK)一样,OpenSL ES用于Android的主要目的是促进使用Java Native Interface (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实现所支持的对象和接口。如果单元格中出现Yes,则该特性在此实现中可用。
Android NDK支持的对象和接口。
Feature | Audio player | Audio recorder | Engine | Output mix |
---|---|---|---|---|
Bass boost | Yes | No | No | Yes |
Buffer queue | Yes | No | No No | |
Buffer queue data locator | Yes: Source | No | No | No |
Dynamic interface management | Yes | Yes | Yes | Yes |
Effect send | Yes | No | No | No |
Engine | No | No | Yes | No |
Environmental reverb | No | No | No | Yes |
Equalizer | Yes | No | No | Yes |
I/O device data locator | No | Yes: Source | No | No |
Metadata extraction | Yes: Decode to PCM | No | No | No |
Mute solo | Yes | No | No | No |
Object | Yes | Yes | Yes | Yes |
Output mix locator | Yes: Sink | No | No | No |
Play | Yes | No | No | No |
Playback rate | Yes | No | No | No |
Prefetch status | Yes | No | No | No |
Preset reverb | No | No | No | Yes |
Record | No | Yes | No | No |
Seek | Yes | No | No | No |
URI data locator | Yes: Source | No | No | No |
Virtualizer | Yes | No | No | Yes |
Volume | Yes | No | No | No |
下一节将解释其中一些特性的局限性。
限制
某些限制适用于表1中的特性。这些限制表示与参考规范的不同。本节的其余部分提供了关于这些差异的信息。
动态接口管理
安卓版的OpenSL ES不支持RemoveInterface或ResumeInterface。
效果组合:环境混响和预置混响
在相同的输出组合上不能同时有环境混响和预置混响。
如果平台估计CPU负载过高,则可能忽略效果请求。
效应发送
SetSendLevel()支持每个音频播放器一个发送级别。
混响环境
环境混响不支持SLEnvironmentalReverbSettings结构体的reflectionsDelay、reflectionsLevel或reverbDelay字段。
MIME数据格式
你只能对URI数据定位器使用MIME数据格式,而且只能对音频播放器使用。您不能将此数据格式用于音频录制器。
OpenSL ES的Android实现要求您将mimeType初始化为NULL或一个有效的UTF-8字符串。还必须将containerType初始化为有效值。在没有其他考虑因素的情况下,比如应用程序无法通过头文件识别的其他实现或内容格式的可移植性,我们建议你将mimeType设置为NULL,将containerType设置为SL_CONTAINERTYPE_UNSPECIFIED。
OpenSL ES为Android支持以下音频格式:
- WAV PCM.
- WAV alaw.
- WAV ulaw.
- MP3 Ogg Vorbis.
- AAC LC.
- HE-AACv1 (AAC+).
- HE-AACv2 (enhanced AAC+).
- AMR.
- FLAC.
注意:关于Android支持的音频格式列表,请参阅支持的媒体格式。
以下限制适用于OpenSL ES实现中对这些格式和其他格式的处理:
- AAC格式必须驻留在MP4或ADTS容器中。
- 安卓版的OpenSL ES不支持MIDI。
- WMA不是AOSP的一部分,我们还没有验证它与OpenSL ES在Android上的兼容性。
- OpenSL ES的Android NDK实现不支持直接播放DRM或加密内容。要想播放受保护的音频内容,在播放之前必须在应用程序中对其进行解密。
Object-related方法
OpenSL ES Android不支持以下操作对象的方法:
- Resume()
- RegisterCallback()
- AbortAsyncOperation()
- SetPriority()
- GetPriority()
- SetLossOfControlInterfaces()
PCM数据格式
PCM是唯一可以用于缓冲区队列的数据格式。支持的PCM播放配置有以下特点:
- 8位无符号或16位有符号。
- 单声道或立体声。
- 低位优先字节顺序。
- 采样率:
- 8000赫兹。
- 11025赫兹。
- 12000赫兹。
- 16000赫兹。
- 22050赫兹。
- 24000赫兹。
- 32000赫兹。
- 44100赫兹。
- 48000赫兹。
OpenSL ES为Android支持的录音配置是依赖于设备的;通常情况下,16000 Hz mono/16-bit signed 可以在任何设备上使用。
samplesPerSec字段的值以milliHz为单位,尽管名称具有误导性。为了避免意外地使用错误的值,我们建议您使用为此定义的符号常量(如SL_SAMPLINGRATE_44_1
)初始化该字段。
Android 5.0 (API级别21)及以上支持[浮点数据]。
Playback rate 回放速度
OpenSL ES回放速率表示对象显示数据的速度,表示为正常速度的千分之一,或叫做每mile。例如,1000每mile的回放率是1000 / 1000,或叫做正常速度。速率范围是表示可能的回放速率范围的一个封闭区间。
对回放速率范围和其他功能的支持可能因平台版本和实现的不同而不同。您的应用程序可以通过使用PlaybackRate::GetRateRange()或PlaybackRate::GetCapabilitiesOfRate()来查询设备,在运行时确定这些功能。
对于PCM格式的数据源,设备通常支持相同的速率范围,对于其他格式,单位速率范围为1000 /mile到1000 /mile;也就是说,单位速率范围实际上是单个值。
Record
Android的OpenSL ES不支持SL_RECORDEVENT_HEADATLIMIT
或SL_RECORDEVENT_HEADMOVING
事件。
Seek
SetLoop()方法支持整个文件循环。要启用循环,请将startPos参数设置为0,将endPos参数设置为SL_TIME_UNKNOWN
。
Buffer queue data locator 缓冲队列数据定位器
带有缓冲区队列数据定位器的音频播放器或录音机只支持PCM数据格式。
I/O device data locator I/O设备数据定位器
当您将定位器指定为Engine::CreateAudioRecorder()的数据源时,OpenSL ES仅支持使用I/O设备数据定位器。使用以下代码片段中包含的值初始化设备数据定位器:
SLDataLocator_IODevice loc_dev =
{SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
URI data locator URI数据定位器
用于Android的OpenSL ES只能使用具有MIME数据格式的URI数据定位器,并且只能用于音频播放器。不能为音频记录器使用URI数据定位器。URI只能使用http:
和file:
协议。其他模式,如https:
、ftp:
或content:
是不允许的。
我们还没有验证对rtsp:
在Android平台上音频的支持。
数据结构
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可以在同一个应用程序中一起使用。在这种情况下,内部只有一个共享引擎对象,并且在OpenMAX AL和OpenSL ES之间共享32个对象限制。应用程序应该创建两个引擎,使用两个引擎,最后销毁两个引擎。实现对共享引擎维护一个引用计数,以便在第二次销毁操作期间正确地销毁它。
编程注意事项
OpenSL ES编程注意事项提供了补充信息,以确保OpenSL ES的正确实现。
注意:为了方便您,我们在
docs/opensles/ opensl_es_specification_1.0.1 pdf
中包含了OpenSL ES 1.0.1规范和NDK的副本。
平台问题
本节描述支持这些api的初始平台发行版中已知的问题。
动态接口管理
DynamicInterfaceManagement::AddInterface无效。相反,在传递给Create()的数组中指定接口,如环境混响的示例代码所示。
计划未来版本的OpenSL ES
Android高性能音频api基于Khronos Group OpenSL ES 1.0.1。Khronos已经发布了该标准的1.1修订版。修改后的版本包含了新的特性、澄清、纠正印刷错误以及一些不兼容的地方。大多数预期的不兼容是相对较小的,或者是在OpenSL ES中不受Android支持的区域。
使用这个版本开发的应用程序应该适用于Android平台的未来版本,前提是您遵循下面的二进制兼容性规划部分中概述的指导方针。
注意:未来的源码兼容性不是目标。也就是说,如果您升级到新版NDK,您可能需要修改应用程序源代码以符合新的API。我们预计,大多数此类变化将是微小的;请参阅下面的详细信息。
二进制兼容性规划
我们建议您的应用程序遵循以下指导方针来改进未来的二进制兼容性:
- 只使用OpenSL ES 1.0.1中所记录的android支持的特性子集。
- 不依赖于不成功操作的特定结果代码;准备好处理不同的结果代码。
- 应用程序回调处理程序通常在受限的上下文中运行。它们应该写下来迅速完成它们的工作,然后尽快返回。不要在回调处理程序中运行复杂的操作。例如,在缓冲区队列完成回调中,您可以对另一个缓冲区进行排队,但不创建音频播放器。
- 回调处理程序应该准备好被频繁调用,以接收额外的事件类型,并且应该忽略它们不认识的事件类型。用启用的事件类型组成的事件掩码配置的回调应该准备好使用多个事件类型位同时被调用。使用“&”测试每个事件位,而不是开关用例。
- 使用预取状态和回调作为进度的一般指示,但不依赖于特定的硬编码填充级别或回调序列。预取状态填充级别的含义以及在预取期间检测到的错误的行为可能会改变。
注意:有关更多细节,请参阅android扩展的缓冲区队列行为部分。
源码兼容性规划
正如前面提到的,在Khronos Group下一个版本的OpenSL ES中预计会出现源代码不兼容的情况。可能的变化领域包括:
- buffer queue interface 缓冲队列接口预计会有显著的变化,特别是在BufferQueue::Enqueue的区域,slBufferQueueCallback的参数列表和字段slbufferqueuat.playindex的名称方面。我们建议您的应用程序代码使用Android简单缓冲区队列。在NDK提供的示例代码中,出于这个原因,我们使用了Android简单的缓冲区队列来回放。(我们还使用Android simple buffer queue简单缓冲区队列记录和解码到PCM,但这是因为标准OpenSL ES 1.0.1不支持记录或解码到缓冲队列数据接收器。)
- 将在通过引用传递的输入参数中添加const,并将SLchar * struct字段用作输入值。这不需要对代码做任何修改。
- 对于一些当前有符号的参数,将替换无符号类型。您可能需要将参数类型从SLint32更改为SLuint32或类似的类型,或者添加强制类型转换。
- Equalizer::GetPresetName将字符串复制到应用程序内存,而不是返回一个指向实现内存的指针。这将是一个重要的更改,因此我们建议您要么避免调用此方法,要么隔离使用它。
- 在struct类型中会有额外的字段。对于输出参数,可以忽略这些新字段,但是对于输入参数,需要初始化这些新字段。幸运的是,所有这些字段都应该位于Android不支持的区域。
- 接口GUIDs将会改变。通过符号名而不是GUID引用接口以避免依赖关系。
- SLchar会从无符号char变为char。这主要影响URI数据定位器和MIME数据格式。
-
SLDataFormat_MIME.mimeType
将被重命名为pMimeType和SLDataLocator_URI.URI
将被重命名为pURI。我们建议您初始化SLDataFormat_MIME
和SLDataLocator_URI
数据结构,使用一个用括号括起来、用逗号分隔的值列表,而不是使用字段名,以便将代码与此更改隔离开来。示例代码中使用了这种技术。 -
SL_DATAFORMAT_PCM
不允许应用程序将数据的表示指定为有符号整数、无符号整数或浮点数。Android实现假设8位数据是无符号整数,16位数据是有符号整数。此外,samplePerSec是一个误称,因为实际单位是毫赫。这些问题预计将在下一个OpenSL ES版本中解决,该版本将引入新的扩展PCM数据格式,允许应用程序显式地指定表示形式并纠正字段名称。由于这将是一种新的数据格式,并且当前的PCM数据格式仍然可用(尽管已经废弃),因此不需要对代码进行任何立即的更改。