Android OpenSL ES 音频播放器

1、OpenSL ES 介绍

OpenSL ES (Open Sound Library for Embedded Systems) 是一个跨平台、无授权费用的音频处理库,它为移动多媒体设备的应用开发者提供了标准化、高性能、低响应的音频功能实现方法,统一的 API 使得通过 OpenSL ES 开发的音频处理程序能够很好的在多个平台上运行。

2、OpenSL 编程说明

OpenSL ES 对象类似于 Java 编程语言中的对象概念,OpenSL ES 中的对象类型为 SLObjectItf,如果需要使用对象中的具体方法,则需要通过 GetInterfaces 的方式得到具体的接口对象,从而使用具体的个性功能,Interface 对象类型需要在创建时指定为显示接口,否则会默认隐藏; 另外 OpenSL ES 中的对象在创建完成后,并不会立即初始化,创建仅仅是构建对象分配基础的内存空间,需要通过 Realize 来对此对象进行初始化。

3、Android 平台下 OpenSL ES 开发准备

CMakeList 引入基础库

target_link_libraries(
${PROJECT_NAME}
​
OpenSLES
)

基础库头文件

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

4、OpenSL 播放流程概览

OpenSL 中的 Engine 是一个非常关键的对象: 1、通过 Engine EngineItf 创建出音频播放器、音频采集器等各种关键对象 2、通过 Engine SLEngineCapabilitiesItf 查询各项能力在相关平台上的兼容性

DataSource 与 DataSink: DataSource 音频播放器中的数据输入源,DataSink 音频播放器中的数据输出对象(一般为系统中的音频设备)

【学习地址】:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发

【文章福利】:免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击1079654574加群领取哦~

5、代码实战

5.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;
}

5.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; // 不需要限制格式,音频输入源会指定好格式

5.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;

5.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.5 播放控制

// 播放
(*playerItf)->SetPlayState(playerItf, SL_PLAYSTATE_PLAYING); 
// 激活回调,此后会循环回调获取PCM数据进行播放
(*androidBufferQueueItf)->Enqueue(androidBufferQueueItf, "", 1);
​
// 暂停
(*playerItf)->SetPlayState(playerItf, SL_PLAYSTATE_PAUSED);

5.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完成音频文件的播放能力。

原文链接:Android OpenSL ES 音频播放器 - 掘金

猜你喜欢

转载自blog.csdn.net/irainsa/article/details/130031510