1. OpenSL ES原理
OpenSL ES(Open Sound Library for Embedded Systems
),即嵌入式音频加速标准,是一个无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API库。它为嵌入移动多媒体设备上的本地应用程序开发者提供了标准化、高性能、低相应时间的音频开发方案,并实现软/硬件音频性能的直接跨平台部署,被广泛应用于3D音效、音频播放、音频录制以及音乐体验增强(低音增强和环境混响)
等方面。对于Android平台而言,我们可以使用OpenSL ES库直接在native层处理音频数据,比如录制音频、播放音频等。OpenSL ES嵌入式设备中部署的软/硬件层次结构,如下图所示:
1.1 OpenSL ES核心API讲解
虽然OpenSL ES是基于C语言开发,但是它的实现思想是基于面向对象的,与FFmpeg框架提供一系列的函数接口不同,OpenSL ES是以Interface的方式来提供API,即除了slCreateEngine这个函数用来创建声音引擎对象接口之外,其他所有的操作都是通过调用接口的成员函数完成的,这与JNI非常相似。比如我们调用OpenSL ES的API是这样的:
SLObjectItf pEngineObject = NULL;
(*pEngineObject)->Realize(pEngineObject, SL_BOOLEAN_FALSE);
【腾讯文档】FFmpegWebRTCRTMPRTSPHLSRTP播放器-音视频流媒体高级开发-资料领取
https://docs.qq.com/doc/DYU5ORlBOdkpCUkNxhttps://docs.qq.com/doc/DYU5ORlBOdkpCUkNx
1.1.1 对象(Object)与接口(Interface)
Object和Interface是OpenSL ES库的两个非常重要的概念,OpenSL ES的整个框架就是基于这两个概念构成的,后续对该库的使用也是基于此来实现。具体关系如下: 1.每个Object可能存在一个或者多个Interface,而每个Interface封装了相关的功能函数
。比如当我们获取一个Audio Player对象后,可以通过该对象得到音频播放Interface、音量Interface、缓存队列Interface,然后调用这些Interface的功能函数实现音频播放、音量调节等功能;
// OpenSL ES引擎Interface
SLEngineItf pEngineItf = NULL;
...
SLObjectItf pPlayerObject = NULL; // Audio Player对象
SLPlayItf pPlayerItf = NULL; // 播放接口
SLVolumeItf pVolumeItf = NULL; // 音量接口
SLAndroidSimpleBufferQueueItf pBufferItf = NULL; // 缓存队列接口
(*pEngineItf)->CreateAudioPlayer(pEngineItf,&pPlayerObject,..);
(*pPlayerObject)->Realize(pPlayerObject,SL_BOOLEAN_FALSE);
(*pPlayerObject)->GetInterface(pPlayerObject, SL_IID_PLAY,&pPlayerItf);
(*pPlayerObject)->GetInterface(pPlayerObject, SL_IID_VOLUME,&pVolumeItf);
(*pPlayerObject)->GetInterface(pPlayerObject,SL_IID_BUFFERQUEUE,&pBufferItf);
2.每个Object对象提供了一些最基础的"管理"操作,比如它的Realize、Destory函数用于分配、释放资源,Resume函数用于结束SL_OBJECT_STATE_SUSPENED状态等等。如果系统使用该对象支持的功能函数,就需要通过该对象的GetInterface函数获取相应的Interface接口,然后通过该Interface接口来访问功能函数。下面以调节音量为例:
// OpenSL ES引擎Interface
SLEngineItf pEngineItf = NULL;
...
SLObjectItf pPlayerObject = NULL;
// 首先,创建Audio Player对象
(*pEngineItf)->CreateAudioPlayer(pEngineItf,&pPlayerObject,..);
// 其次,初始化Audio Player对象,即分配资源
(*pPlayerObject)->Realize(pPlayerObject,SL_BOOLEAN_FALSE);
// 第三,获取Audio Player对象的音量(Volume)Interface
(*pPlayerObject)->GetInterface(pPlayerObject, SL_IID_VOLUME,&pVolumeItf);
// 最后,调用Volume Interface的调节音量功能函数
(*pVolumeItf)->SetVolumeLevel(&pVolumeItf,level);
注意:由于OpenSL ES库是跨平台的,但是并非所有平台都实现了某个对象(Object)定义的所有接口(Interface),因此在使用的过程中,最好还是对获取的Interface作一些选择和判断,以免出现预料不到的错误。
1.1.2 OpenSL ES的状态机制
OpenSL ES有个比较重要的概念-状态机制,即对于任何OpenSL ES的对象,在被创建成功后都会进入SL_OBJECT_STATE_UNREALIZE
状态,此时系统不会为该对象分配任何资源;当调用对象的Realize()成员函数后,该对象就会进入SL_OBJECT_STATE_REALIZE
状态,此时对象的各个功能和资源才能正常被访问;当然,当突然出现一些系统事件,比如系统错误或者Audio设备被其他应用抢占,该对象就会进入SL_OBJECT_STATE_SUSPENED
状态,如果我们希望恢复正常使用,就需要调用对象的Resume函数;最后,我们可以调用对象的Destory函数,来释放资源,此时对象的状态会回到SL_OBJECT_STATE_UNREALIZE状态。OpenSL ES状态机制转化流程如下图所示:
1.1.3 OpenSL ES重要接口
(1) SLObjectItf:对象接口,是Sound Library Object Interface的缩写,表示一个泛指对象,就像Java中的Object一样,OpenSL ES中的所有对象均由它来表示,但具体代表是哪个对象,就由SLEngineItf的相关函数决定(注:SLEngineItf是Engine Object的接口)。创建对象:
// 创建OpenSL SL引擎对象(Engine Object)
SLObjectItf pEngineObject = NULL;
slCreateEngine(&pEngineObject, 0, NULL, 0, NULL, NULL);
// 创建混音器对象(OutputMix Object)
SLObjectItf pOutputMixObject = NULL;
(*pEngineItf)->CreateOutputMix(pEngineItf,&pOutputMixObject,...);
// 创建播放器对象(Player Object)
SLObjectItf pPlayerObject = NULL;
(*pEngineItf)->CreateAudioPlayer(pEngineItf,&pPlayerObject,...);
如果是销毁某个对象,调用它的Destory成员函数即可。
if (pEngineObject) {
(*pEngineObject)->Destroy(pEngineObject);
}
if (pOutputMixObject) {
(*pOutputMixObject)->Destroy(pOutputMixObject);
}
if (pPlayerObject) {
(*pPlayerObject)->Destroy(pPlayerObject);
}
重难讲一下,Engine Object是OpenSL ES API的入口点
,是OpenSL ES中最核心的对象,用于管理Audio Engine生命周期和创建OpenSL ES中所有其他的对象。Engine Object由slCreateEngine函数创建,创建的结果是得到Engine Object的一个接口-SLEngineItf
,在这个接口的结构体中封装了创建其他各种对象的函数。SLObjectItf
结构体定义如下,位于.../SLES/OpenSLES.h
头文件中:
struct SLObjectItf_