目录
- OpenSL ES 介绍
- OpenSL 编程说明
- Android 平台下 OpenSL ES 开发准备
- OpenSL 播放流程概览
- 代码实战
OpenSL ES 介绍
OpenSL ES (Open Sound Library for Embedded Systems) 是一个跨平台、无授权费用的音频处理库,它为移动多媒体设备的应用开发者提供了标准化、高性能、低响应的音频功能实现方法,统一的 API 使得通过 OpenSL ES 开发的音频处理程序能够很好的在多个平台上运行。
OpenSL 编程说明
OpenSL ES 对象类似于 Java 编程语言中的对象概念,OpenSL ES 中的对象类型为 SLObjectItf,如果需要使用对象中的具体方法,则需要通过 GetInterfaces 的方式得到具体的接口对象,从而使用具体的个性功能,Interface 对象类型需要在创建时指定为显示接口,否则会默认隐藏; 另外 OpenSL ES 中的对象在创建完成后,并不会立即初始化,创建仅仅是构建对象分配基础的内存空间,需要通过 Realize 来对此对象进行初始化。
Android 平台下 OpenSL ES 开发准备
CMakeList 引入基础库
target_link_libraries(
${PROJECT_NAME}
OpenSLES
)
基础库头文件
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
OpenSL 播放流程概览
OpenSL 中的 Engine 是一个非常关键的对象: 1、通过 Engine EngineItf 创建出音频播放器、音频采集器等各种关键对象 2、通过 Engine SLEngineCapabilitiesItf 查询各项能力在相关平台上的兼容性
DataSource 与 DataSink: DataSource 音频播放器中的数据输入源,DataSink 音频播放器中的数据输出对象(一般为系统中的音频设备)
代码实战
1、创建Engine并获取Engine接口对象
// 创建并初始化Engine对象
SLObjectItf engineObj;
SLresult result = slCreateEngine(&engineObj, 0, NULL, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS) {
// 创建Engine失败
return;
}
result = (*engineObj)->Realize(engineObj, SL_BOOLEAN_FALSE); // TRU表示异步初始化,这里同步初始化即可
if (result != SL_RESULT_SUCCESS) {
// Engine初始化失败
return;
}
// 获取Engine接口对象
SLEngineItf engineItf;
result = (*engineObj)->GetInterfaces(engine, SL_IID_ENGINE, &engine);
if (result != SL_RESULT_TRUE) {
// Engine接口对象获取失败
return;
}
2、通过SLEngineItf创建音频输出对象SLDataSink(即默认设备)
// 创建混音器(它默认与系统音频设备绑定)
SLObjectItf outputMix;
SLInterfaceID outputMixItfIds[1] = {SL_IID_ENVIRONMENTALREVERB}; // 导出混音效果设置接口
SLboolean outputMixItfReq[1] = {SL_BOOLEAN_FALSE}; // FALSE:该接口不支持,也不影响对象的初始化流程;TRUE:接口不支持,会导致初始化失败
result = (*engineItf)->CreateOutputMix(engineItf, &outputMix, 1, outputMixItfIds, outputMixItfReq);
if (result != SL_RESULT_SUCCESS) {
// 混音器对象创建失败
return;
}
result = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE); // 同步初始化
if (result != SL_RESULT_SUCCESS) {
// 混音器对象初始化失败
return;
}
// 混音效果设置接口对象
SLEnvironmentalReverbItf envReverbItf;
result = (*outputMix)->GetInterface(outputMix, SL_IID_ENVIRONMENTREVERB, &envRevertbItf);
if (result == SL_RESULT_SUCCESS) {
// 接口对象获取成功,设置混音效果
SLEnvironmentalReverbSettings stonecorridor = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; // 走廊效果
(*envReverbItf)->SetEnvironmentReverbProperties(envReverbItf, &stonecorridor);
return;
}
// 音频数据输出对象
SLDataLocator_OutputMix locaterOutputMix;
locaterOutputMix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
locaterOutputMix.outputMix = output_mix_itf;
SLDataSink dataSink;
dataSink.pLocater = &locaterOutputMix;
dataSink.pFormat = NULL; // 不需要限制格式,音频输入源会指定好格式
3、创建音频数据源SLDataSource
// 指定数据送入形式,AndroidSimpleBufferQueue
SLDataLocater_AndroidSimpleBufferQueue locaterAndroidBufferQueue;
locaterAndroidBufferQueue.locaterType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
locaterAndroidBufferQueue.numBuffers = 2; // 越多播放越流畅、同时内存占用也越多
// 指定PCM数据格式限制
SLDataFormat_PCM formatPcm;
formatPcm.formatType = SL_DATAFORMAT_PCM;
formatPcm.numChannels = 2; // 声道数 2
formatPcm.samplesPerSec = SL_SAMPLINGRATE_44_1; // 采样率 44100HZ
formatPcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; // 采样位深 16(2字节)
formatPcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; // 与位深一样即可
formatPcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; // 立体声: 左前 | 右前
formatPcm.endianness = SL_BYTEORDER_LITTLEENDIAN; // 小端
// 音频数据输入对象
SLDataSource dataSource;
dataSource.pLocater = &locaterAndroidBufferQueue;
dataSource.pFormat = &formatPcm;
4、通过SLEngineItf创建音频播放器
// 创建播放器对象
SLObjectItf playerObj;
SLInterfaceID playerItfIds[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};// 导出AndroidSimpleBufferQueue接口,需要此接口送入PCM原始数据进行播放
SLboolean playerItfReq[] = {SL_BOOLEAN_TRUE}; // 强依赖此接口,因此接口检查为TRUE,创建时如果发现不支持此接口,创建流程会返回失败
result = (*engineItf)->CreateAudioPlayer(engineItf, &playerObj, &dataSource, &dataSink, 1, playerItfIds, playerItfReq);
if (result != SL_RESULT_SUCCESS) {
// 创建Player对象失败
return;
}
result = (*playerObj)->Realize(playerObj, SL_BOOLEAN_FALSE); // 同步初始化
if (result != SL_RESULT_SUCCESS) {
// Player初始化失败
return;
}
// 导出播放控制接口对象
SLPlayerItf playerItf;
result = (*playerObj)->GetInterface(playerObj, SL_IID_PLAYER, &playerItf);
if (result != SL_RESULT_SUCCESS) {
// 导出播放控制接口对象失败
return;
}
// 导出BufferQueue接口对象
SLAndroidSimpleBufferQueueItf androidBufferQueueItf;
result = (*playerObj)->GetInterface(playerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &androidBufferQueueItf);
if (result != SL_RESULT_SUCCESS) {
// 导出BufferQueue对象接口失败
return;
}
// BufferQueue 注册回调,OpenSL播放时通过回调主动要数据的形式进行播放的,开发者无法直接塞入数据
void AndroidSimpleBufferQueueCallback(const SLAndroidSimpleBufferQueueItf queue, void *data) {
// 输入pcm原始数据,此原始数据需要与前面规范的格式一一对应上,否则播放异常
(*queue)->Enqueue(queue, uint8_t* data, int dataSize);
}
result = (*androidBufferQueueItf)->RegisterCallback(androidBufferQueueItf, AndroidSimpleBufferQueueCallback, this);
if (result != SL_RESULT_SUCCESS) {
// BufferQueue回调注册失败
return;
}
5、播放控制
// 播放
(*playerItf)->SetPlayState(playerItf, SL_PLAYSTATE_PLAYING);
// 激活回调,此后会循环回调获取PCM数据进行播放
(*androidBufferQueueItf)->Enqueue(androidBufferQueueItf, "", 1);
// 暂停
(*playerItf)->SetPlayState(playerItf, SL_PLAYSTATE_PAUSED);
6、资源释放
// 释放播放器
if (playerObj) {
(*playerObj)->Destroy(playerObj);
playerObj = NULL;
}
// 释放混音器
if (outputMix) {
(*outputMix)->Destroy(outputMix);
outputMix = NULL;
}
// 释放Engine
if (engineObj) {
(*engineObj)->Destroy(engineObj);
engineObj = NULL;
}
通过以上方式就能在Android平台上完成OpenSL ES音频播放器的创建,下一节将会结合FFmpeg完成音频文件的播放能力。