一、什么是 OpenSL ES
它是一套无授权费,跨平台,针对嵌入式系统精心设计的硬件音频加速 API
二、它的优势
- 在 NDK 层开发,具备更快的速度。
- 与平台无关
三、如何进行开发
1.引入 OpenSL ES android 扩展包
针对 Android 平台,我们只需要引入 Android 的扩展包即可
#include <SLES/OpenSLES_Android.h>
2.OpenSL ES 的初始化
OpenSL ES几乎都是通过一个Object一个Interface成对来获取一项功能,比如OpenSL ES的全局引擎engineObject和engineInterface
所有功能都是先创建 object 再获取 object 对应的接口,创建 object 时,该对象处于未激活的状态,需要调用它的 Realize 函数激活它
它的 IO 回调模型:当缓冲区的数据没有填满时,会不断调用这个回调请求数据,直到填满,当缓冲区填满时停止,当缓冲区没有数据时发起下一轮调用
// 音频引擎对象
SLObjectItf engineObject = NULL;
// 音频引擎接口
SLEngineItf engineInterface = NULL;
// 播放器对象
SLObjectItf bqPlayerObject = NULL;
// 播放器的接口
SLPlayItf bqPlayerInterface = NULL;
// 混音器 调音用的 必须传递,可以使用默认的混音器
SLObjectItf outputMixObject = NULL;
// 缓冲队列 负责获取数据
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
// ----------------1-----初始化播放引擎-----------------------------
// 几乎所有的 OpenSL ES 函数都返回这个对象,可以用来调试
SLresult result;
result= slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (SL_RESULT_SUCCESS != result) {
return;
}
// 激活该对象
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
return;
}
// 拿到 object 对应的接口方法,相当于 SurfaceHolder
result=(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineInterface);
if (SL_RESULT_SUCCESS != result) {
return;
}
// 创建混音器
result = (*engineInterface)->CreateOutputMix(engineInterface, &outputMixObject, 0, 0, 0);
// 初始化混音器outputMixObject
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
return;
}
// ---------3------------创建输入数据-----------------------------
// 创建一个音频源队列
SLDataLocator_AndroidSimpleBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
2};
//数据格式 这里选 pcm
SLDataFormat_PCM pcm = {SL_DATAFORMAT_PCM//播放pcm格式的数据
, 2,//2个声道(立体声)
SL_SAMPLINGRATE_44_1, //44100hz的频率
SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位 采样位数
SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行 采样位数
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右) 模式
SL_BYTEORDER_LITTLEENDIAN//小端模式
};
// 设置音频源 也就是输入数据 参数一表示音频队列 参数二 数据格式
SLDataSource slDataSource = {&android_queue, &pcm};
// ------4 ------- 创建输出数据对象--------
SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
// 输出数据
SLDataSink audioSnk = {&outputMix, NULL};
//-------5 ------ 创建播放器 -----
const SLInterfaceID ids[1] = {SL_IID_BUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
// 创建播放器对象
(*engineInterface)->CreateAudioPlayer(engineInterface
,&bqPlayerObject// //播放器
,&slDataSource//播放器参数 播放缓冲队列 播放格式
,&audioSnk,//播放缓冲区
1,//播放接口回调个数
ids,//设置播放队列ID
req//是否采用内置的播放队列
);
// 激活播放器状态
(*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
//bqPlayerObject 这个对象
// 得到接口回调 获取Player接口
(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerInterface);
// 初始化播放器的缓冲队列
(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue);
// ------------------3 注册回调函数---------------------------------------
(*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this);
// ------------------4 设置播放状态 ------------------------------
(*bqPlayerInterface)->SetPlayState(bqPlayerInterface, SL_PLAYSTATE_PLAYING);
// ------------------5 触发回调后 就相当于调用了播放按钮 回不断向缓冲区写数据 ----------
bqPlayerCallback(bqPlayerBufferQueue, this);
// 创建了一个接收数据的接口 这个函数被第一次调用时 就将不断被播放
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
//
AudioChannel *audioChannel = static_cast<AudioChannel *>(context);
// 当调用播放器的播放的时候 请求转码
int datalen = audioChannel->getPcm();
if (datalen > 0) {
// 送入缓冲队列进行播放
(*bq)->Enqueue(bq, audioChannel->buffer, datalen);
}
//pcm数据 原始音频数据
}
网友评论