OpenSL ES的全称是Open Sound Library For Embedded Systems,即应用于嵌入式系统的开源音频库。Android从2.3版本起就开始支持OpenSL ES标准了,并且通过NDK提供相应的API开发接口。OpenSL ES有以下特性:
和Android提供的AudioRecord和AudioTrack相比,OpenSL ES提供了更高的性能,更快的速度。因为AudioRecord和AudioTrack都是Android提供的Java API,无论是采集还是播放音频,都需要将音频数据从java层拷贝到native层,或从native层拷贝到java层,这无疑是十分消耗资源的。如果希望减少拷贝,开发更加高效的Android音频应用,则建议使用Android NDK提供的OpenSL ES API接口,它支持在native层直接处理音频数据。
- //opensles.cpp
#include<cstdint> - #include<iostream>
- #include<jni.h>
-
- extern "C"{
- #include<SLES/OpenSLES.h>
- #include<android/log.h>
- #include<SLES/OpenSLES_Android.h>
- }
- #define TAG "jni" // 这个是自定义的LOG的标识
- #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
- #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
- #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
- #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
- using namespace std;
- //engine interface
- static SLObjectItf engineObject= nullptr;
- static SLEngineItf engineEngine= nullptr;
- //output mix interfaces
- static SLObjectItf outputMixObject= nullptr;
- static SLEnvironmentalReverbItf outputMixEnvironmentalReverb= nullptr;
- //player interface
- static SLObjectItf pcmPlayerObject= nullptr;
- static SLPlayItf pcmPlayerplay= nullptr;
- //buffer queue
- static SLAndroidSimpleBufferQueueItf pcmBufferQueue= nullptr;
- //pcm file
- FILE *pcmFile= nullptr;
- void *buffer= nullptr;
- uint8_t *out_buffer= nullptr;
- static const SLEnvironmentalReverbSettings reverbSettings=SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
- //播放回调
- void playerCallback(SLAndroidSimpleBufferQueueItf bufferQueueItf,void *context){
- if(bufferQueueItf!=pcmBufferQueue){
- LOGI("SLAndroidSimpleBufferQueueItf is not equal");
- return;
- }
- while(!feof(pcmFile)){
- size_t size=fread(out_buffer,44100*2*4,1,pcmFile);
- if(out_buffer== nullptr||size==0){
- LOGI("read end %ld",size);
- }else{
- LOGI("reading %ld",size);
- }
- buffer=out_buffer;
- break;
- }
- if(buffer){
- LOGI("buffer is not null");
- SLresult result=(*pcmBufferQueue)->Enqueue(pcmBufferQueue,buffer,44100*2*4);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("pcmBufferQueue error %ld",result);
- }
- }
- }
- jint playPcmBySL(JNIEnv *env,jobject thiz,jstring pcm_path){
- const char *pcmPath=env->GetStringUTFChars(pcm_path, nullptr);
- pcmFile=fopen(pcmPath,"r");
- env->ReleaseStringUTFChars(pcm_path,pcmPath);
- if(pcmFile== nullptr){
- LOGI("open pcmFile error");
- return -1;
- }
- out_buffer=(uint8_t *)malloc(44100*2*4);
- //创建引擎对象
- SLresult result=slCreateEngine(&engineObject,0,nullptr,0,nullptr,nullptr);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("slCreateEngine failed %ld",result);
- return -1;
- }
- //实例化引擎
- result=(*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("engine realize failed %ld",result);
- return -1;
- }
- //获取引擎接口SLEngineItf
- result=(*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&engineEngine);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("GetInterface SLEngineItf failed %ld",result);
- return -1;
- }
- //创建输出混音器
- const SLInterfaceID ids[1]={SL_IID_ENVIRONMENTALREVERB};
- const SLboolean req[1]={SL_BOOLEAN_FALSE};
- result=(*engineEngine)->CreateOutputMix(engineEngine,&outputMixObject,1,ids,req);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("CreateOutputMix failed %ld",result);
- return -1;
- }
- //实例化混音器
- result=(*outputMixObject)->Realize(outputMixObject,SL_BOOLEAN_FALSE);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("Realize outputMixObject failed %ld",result);
- return -1;
- }
- //获取混音器接口SLEnvironmentalReverbItf
- result=(*outputMixObject)->GetInterface(outputMixObject,SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("GetInterface SLEnvironmentalReverbItf failed %ld",result);
- return -1;
- }
- //给混音器设置环境混响属性
- (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb,&reverbSettings);
- //设置输入 SLDataSource
- SLDataLocator_AndroidSimpleBufferQueue loc_bufq={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
- SLDataFormat_PCM formatPcm={
- SL_DATAFORMAT_PCM,
- 2,
- SL_SAMPLINGRATE_44_1,
- SL_PCMSAMPLEFORMAT_FIXED_32,
- SL_PCMSAMPLEFORMAT_FIXED_32,
- SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,
- SL_BYTEORDER_LITTLEENDIAN
- };
- SLDataSource slDataSource={&loc_bufq,&formatPcm};
- //设置输出SLDataSink
- SLDataLocator_OutputMix loc_outmix={SL_DATALOCATOR_OUTPUTMIX,outputMixObject};
- SLDataSink audioSnk={&loc_outmix, nullptr};
- //创建音频播放器对象
- const SLInterfaceID ids2[1] = {SL_IID_BUFFERQUEUE};
- const SLboolean req2[1] = {SL_BOOLEAN_TRUE};
- result=(*engineEngine)->CreateAudioPlayer(engineEngine,&pcmPlayerObject,&slDataSource,&audioSnk,1,ids2,req2);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("CreateAudioPlayer failed %ld",result);
- return -1;
- }
- //实例化音频播放器对象
- result=(*pcmPlayerObject)->Realize(pcmPlayerObject,SL_BOOLEAN_FALSE);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("Realize pcmPlayerObject failed %ld",result);
- return -1;
- }
- //获取音频播放器接口pcmPlayerplay
- result=(*pcmPlayerObject)->GetInterface(pcmPlayerObject,SL_IID_PLAY,&pcmPlayerplay);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("GetInterface pcmPlayerplay failed %ld",result);
- return -1;
- }
- //获取音频播放的buffer接口SLAndroidSimpleBufferQueueItf
- result=(*pcmPlayerObject)->GetInterface(pcmPlayerObject,SL_IID_BUFFERQUEUE,&pcmBufferQueue);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("GetInterface pcmBufferQueue failed %ld",result);
- return -1;
- }
- //注册回调RegisterCallback
- result=(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue,playerCallback, nullptr);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("RegisterCallback failed %ld",result);
- return -1;
- }
- //设置播放状态为playing
- result=(*pcmPlayerplay)->SetPlayState(pcmPlayerplay,SL_PLAYSTATE_PLAYING);
- if(result!=SL_RESULT_SUCCESS){
- LOGI("SetPlayState failed %ld",result);
- return -1;
- }
- //触发回调
- playerCallback(pcmBufferQueue, nullptr);
- return 0;
- }
- cmake_minimum_required(VERSION 3.22.1)
- project("mediaplayer")
- add_library(${CMAKE_PROJECT_NAME} SHARED
- # 将自己写的cpp源文件编译成动态库
- opensles.cpp)
- target_link_libraries(${CMAKE_PROJECT_NAME}
- # List libraries link to the target library
- android
- log
- OpenSLES
- )
- val pcmPath=getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)?.absolutePath+File.separator+"input.pcm"
- playPcmBySL(pcmPath)
需要注意的是,pcm文件可以通过使用ffmpeg解码mp3文件得到,但是在解码的时候需要注意的是:解码时位深别用32位浮点型,播放出来会有很大的噪音,最好用有符号的32位整型。原因尚未找到,可能是opensl es不支持32位浮点型位深吧。
可以用以下命令解码得到pcm文件:ffmpeg -i input.mp3 -acodec pcm_s32le -f s32le -ac 2 -ar 44100 -y output.pcm