创建接口过程
创建接口有两种方法:
CreateXXX
,这种获取的都是通用类型的接口,需要实例化GetInterface
,获取的是具体类型的接口,因为它需要传入接口类型ID,不需要实例化
实例化过程
实例化就是自己给自己实例化,所有类型的实例化是固定的方法:
//obj是通用类型 //第二个参数表示是否异步执行 一般为false (*obj)->Realize(obj, SL_BOOLEAN_FALSE);
播放的初始化工作是比较麻烦的,参数非常多,关键参数一定要弄清楚,否则不知其所以然。
1.1 引擎对象
想要调用OpenSL的API,它有一个唯一的门口slCreateEngine
,很多文章里叫它引擎,我就叫引擎门口,直观一点,门口里面还有其它的小门口。
SLObjectItf engineObj; //API门口 //1.1获取引擎对象接口 SLresult result = slCreateEngine(&engineObj, 0, 0, 0, 0, 0); //1.2 SLObjectItf 类型,需要实例化门口引擎对象接口 result = (*engineObj)->Realize(engineObj, SL_BOOLEAN_FALSE);
1.2 获取引擎管理接口
有了引擎对象,接下来就要获取需要的引擎管理接口了,OpenSL有多种引擎管理接口,通过ID区分,例如下面的SL_IID_ENGINE
SLEngineItf engineEngine; //2.1获取SLEngineItf类型引擎接口,后续操作将会使用这个接口 result = (*engineObj)->GetInterface(engineObj, SL_IID_ENGINE, &engineEngine); //SLEngineItf 是具体类型不需要实例化
1.3 音频混音
混音器用于将多个音频混合并且输出到喇叭
SLObjectItf outputMixObj; const SLInterfaceID ids[] = {SL_IID_VOLUME}; const SLboolean req[] = {SL_BOOLEAN_FALSE}; //3.1创建音频输出混音对象接口 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObj, 0, ids, req); //3.2 SLObjectItf 类型,实例化音频输出混音对象接口 result = (*outputMixObj)->Realize(outputMixObj, SL_BOOLEAN_FALSE); //3.3 配置输出管道 SLDataLocator_OutputMix outputMixLocator = {SL_DATALOCATOR_OUTPUTMIX, outputMixObj}; SLDataSink outputSink = {&outputMixLocator, NULL}; // 配置输出源 //4.1配置缓冲区Buffer Queue参数 outputLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; //4.2设置音频源的音频格式 SLDataFormat_PCM outputFormat = { SL_DATAFORMAT_PCM, //指定PCM格式 2, //通道个数 SL_SAMPLINGRATE_44_1, //采样率 SL_PCMSAMPLEFORMAT_FIXED_16,//采样精度 SL_PCMSAMPLEFORMAT_FIXED_16,//窗口大小 SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//通道掩码 SL_BYTEORDER_LITTLEENDIAN //字节序:小端 }; //4.3输出源 SLDataSource outputSource = {&outputLocator, &outputFormat};
1.4 获取播放器对象门口
播放器门口不是具体执行播放的工具,而是管理播放相关的缓冲,音频格式,混音,输出等
//5.1获取播放器对象接口 SLObjectItf audioPlayerObj; const SLInterfaceID outputInterfaces[1] = {SL_IID_BUFFERQUEUE}; const SLboolean requireds[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE}; result = (*engineEngine)->CreateAudioPlayer(engineEngine, &audioPlayerObj, &outputSource,//输出源 &outputSink,//输出管道 1,//接口个数 outputInterfaces,//输出接口 requireds); //接口配置 //看到了没,又是SLObjectItf 类型,还得实例化 //5.2实例化播放器对象接口 result = (*audioPlayerObj)->Realize(audioPlayerObj, SL_BOOLEAN_FALSE);
1.5 音频输出对象
音频输出对象就是音频数据本身,具体一点就是存放即将被播放的数据所在的缓冲区
//6.1获取具体音频输出对象接口 SLAndroidSimpleBufferQueueItf outputBufferQueueInterface; result = (*audioPlayerObj)->GetInterface(audioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &outputBufferQueueInterface); //SLAndroidSimpleBufferQueueItf 是具体类型,不用实例化
1.6 具体的播放器对象
它是用来执行播放功能的,其它的条件都给它准备好了
SLPlayItf audioPlayerPlay; //7.1获取播放器播放对象接口 result = (*audioPlayerObj)->GetInterface(audioPlayerObj, SL_IID_PLAY, &audioPlayerPlay); if (result != SL_RESULT_SUCCESS) { LOGD("audioPlayerObj SL_IID_PLAY GetInterface failed,result=%d", result); return result; } //具体类型,不用实例化
1.7 设置回调
回调函数的作用是:通知。
通知什么?在播放的时候,OpenSL不会一次性把所有数据都读到缓冲区,需要用一点,拷贝一点,这个函数就是播放器告诉你,缓存用光了,需要新的数据。
所以在回调函数中需要把新的数据拷贝到缓冲区。
//8.1设置回调 result = (*outputBufferQueueInterface)->RegisterCallback(outputBufferQueueInterface, PlayCallback, this);
二、开始播放
//9设置为播放状态 (*audioPlayerPlay)->SetPlayState(audioPlayerPlay, SL_PLAYSTATE_PLAYING); LOGI("setPlayerState:SL_PLAYSTATE_PLAYING"); //10启动回调机制,开始播放 PlayCallback(outputBufferQueueInterface, this);
三、写数据
前面说了,回调函数中需要填充新的数据:
SLuint32 getPcmData(void **pcm, FILE *pcmFile, uint8_t *out_buffer) { while (!feof(pcmFile)) { //因为PCM采样率为44100,采样精度为16BIT,所以一次读取2秒钟的采样 size_t size = fread(out_buffer, 1, 44100 * 2 * 2, pcmFile); *pcm = out_buffer; return size; } return 0; } //当outputBufferQueueInterface中的数据消耗完就会触发回调 void PlayCallback(SLAndroidSimpleBufferQueueItf bufferQueue, void *pContext) { LOGI("PlayCallback"); //获取数据 SLuint32 size = getPcmData(&readPCMBuffer, pcmFile, tempBuffer); LOGI("PlayCallback, size=%d", size); if (NULL != readPCMBuffer && size > 0) { SLresult result = (*outputBufferQueueInterface)->Enqueue(outputBufferQueueInterface, readPCMBuffer, size); } }
停止播放
//11.停止播放 (*audioPlayerPlay)->SetPlayState(audioPlayerPlay, SL_PLAYSTATE_STOPPED);
释放OpenSL ES资源
只需要销毁OpenSL ES对象,接口不需要做Destroy处理
(*engineObj)->Destroy(engineObj); (*outputMixObj)->Destroy(outputMixObj); (*audioPlayerObj)->Destroy(audioPlayerObj);