IV . OpenSL ES 播放代码 ( 详细注释 )
OpenSL ES 播放部分的代码 : 细节内容看注释吧 , 不再展开一条一条的写了 ;
// I . 创建 OpenSLES 引擎并获取引擎的接口 ( 相关代码拷贝自 Google 官方示例 native-audio ) // 参考 : https://github.com/android/ndk-samples/blob/master/native-audio/app/src/main/cpp/native-audio-jni.c //声明每个方法执行的返回结果 , 一般情况下返回 SL_RESULT_SUCCESS 即执行成功 // 该类型本质是 int 类型 , 定义的是各种类型的异常 SLresult result; // 创建引擎 result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); // 返回 0 成功 , 否则失败 , 一旦失败就中断退出 assert(SL_RESULT_SUCCESS == result); (void)result; // 实现引擎 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS == result); (void)result; // 获取引擎接口 , 使用该接口创建输出混音器 , 音频播放器等其它对象 // 引擎对象不提供任何调用的方法 , 引擎调用的方法都定义在接口中 result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); assert(SL_RESULT_SUCCESS == result); (void)result; // II . 设置输出混音器 // 输出声音 , 添加各种音效 ( 混响 , 重低音 , 环绕音 , 均衡器 等 ) , 都要通过混音器实现 ; // 创建输出混音器对象 , 可以指定一个混响效果参数 ( 该混淆参数可选 ) const SLInterfaceID ids_engine[1] = {SL_IID_ENVIRONMENTALREVERB}; const SLboolean req_engine[1] = {SL_BOOLEAN_FALSE}; result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids_engine, req_engine); assert(SL_RESULT_SUCCESS == result); (void)result; // 实现输出混音器 result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS == result); (void)result; // III . 获取混响接口 并 设置混响 ( 可能会失败 ) // 获取环境混响接口 // 如果环境混响效果不可用 , 该操作可能失败 // either because the feature is not present, excessive CPU load, or // the required MODIFY_AUDIO_SETTINGS permission was not requested and granted result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb); if (SL_RESULT_SUCCESS == result) { result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties( outputMixEnvironmentalReverb, &reverbSettings); (void)result; } //IV . 配置音源输入 // 配置要播放的音频输入缓冲队列属性参数 , 缓冲区大小 , 音频格式 , 采样率 , 样本位数 , 通道数 , 样本大小端格式 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; /* typedef struct SLDataFormat_PCM_ { SLuint32 formatType; //数据格式 SL_DATAFORMAT_PCM SLuint32 numChannels; //通道数 , 左右声道 2个 2 SLuint32 samplesPerSec; //采样率 44100Hz SL_SAMPLINGRATE_44_1 SLuint32 bitsPerSample; //采样位数 16位 SL_PCMSAMPLEFORMAT_FIXED_16 SLuint32 containerSize; //容器大小 SL_PCMSAMPLEFORMAT_FIXED_16 SLuint32 channelMask; //通道 SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT SLuint32 endianness; //小端格式 SL_BYTEORDER_LITTLEENDIAN } SLDataFormat_PCM; */ SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, //PCM 格式 2, //两个声道 SL_SAMPLINGRATE_44_1, //采样率 44100 Hz SL_PCMSAMPLEFORMAT_FIXED_16, //采样位数 16位 SL_PCMSAMPLEFORMAT_FIXED_16, //容器为 16 位 SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, //左右双声道 SL_BYTEORDER_LITTLEENDIAN}; //小端格式 // 设置音频数据源 , 配置缓冲区 ( loc_bufq ) 与 音频格式 (format_pcm) SLDataSource audioSrc = {&loc_bufq, &format_pcm}; // V . 配置音频输出 // 配置混音器 : 将 outputMixObject 混音器对象装载入 SLDataLocator_OutputMix 结构体中 SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; // 将 SLDataLocator_OutputMix 结构体装载到 SLDataSink 中 // 音频输出通过 loc_outmix 输出 , 实际上是通过 outputMixObject 混音器对象输出的 SLDataSink audioSnk = {&loc_outmix, NULL}; // VI . 创建并实现播放器 /* * 创建音频播放器: * 如果需要效果器时 , 不支持高性能音频 * ( fast audio does not support when SL_IID_EFFECTSEND is required, skip it * for fast audio case ) */ // 操作队列接口 , 如果需要 特效接口 , 添加 SL_IID_EFFECTSEND const SLInterfaceID ids_player[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_EFFECTSEND, /*SL_IID_MUTESOLO,*/}; const SLboolean req_player[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, /*SL_BOOLEAN_TRUE,*/ }; // 创建播放器 result = (*engineEngine)->CreateAudioPlayer( engineEngine, &bqPlayerObject, &audioSrc, //音频输入 &audioSnk, //音频商户处 bqPlayerSampleRate? 2 : 3,// ids_player, req_player); assert(SL_RESULT_SUCCESS == result); (void)result; // 创建播放器对象 result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS == result); (void)result; // VII . 获取播放器接口 和 缓冲队列接口 // 获取播放器 Player 接口 : 该接口用于设置播放器状态 , 开始 暂停 停止 播放 等操作 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); assert(SL_RESULT_SUCCESS == result); (void)result; // 获取播放器 缓冲队列 接口 : 该接口用于控制 音频 缓冲区数据 播放 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue); assert(SL_RESULT_SUCCESS == result); (void)result; // VIII . 注册回调函数 // 注册缓冲区队列的回调函数 , 每次播放完数据后 , 会自动回调该函数 // 传入参数 this , 就是 bqPlayerCallback 函数中的 context 参数 result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this); assert(SL_RESULT_SUCCESS == result); (void)result; // IX . 获取效果器接口 和 音量控制接口 ( 不是必须的 ) // 获取效果器发送接口 ( get the effect send interface ) bqPlayerEffectSend = NULL; if( 0 == bqPlayerSampleRate) { result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, &bqPlayerEffectSend); assert(SL_RESULT_SUCCESS == result); (void)result; } #if 0 // mute/solo is not supported for sources that are known to be mono, as this is // get the mute/solo interface result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo); assert(SL_RESULT_SUCCESS == result); (void)result; #endif // 获取音量控制接口 // 获取音量控制接口 ( get the volume interface ) [ 如果需要调节音量可以获取该接口 ] result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); assert(SL_RESULT_SUCCESS == result); (void)result; // X . 设置播放状态 // 设置播放器正在播放状态 ( set the player's state to playing ) result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); assert(SL_RESULT_SUCCESS == result); (void)result; // XI. 手动调用激活回调函数 // 手动激活 , 手动调用一次 bqPlayerCallback 回调函数 bqPlayerCallback(bqPlayerBufferQueue, this);
IV . OpenSLES slAndroidSimpleBufferQueueCallback 回调函数声明及实现代码
1 . 回调函数原型 :
typedef void (SLAPIENTRY *slAndroidSimpleBufferQueueCallback)(
SLAndroidSimpleBufferQueueItf caller,
void *pContext
);
1
2
3
4
2 . 回调函数声明及实现 :
//每当缓冲数据播放完毕后 , 会自动回调该回调函数
// this callback handler is called every time a buffer finishes playing
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
//获取 PCM 采样数据 , 将重采样的数据放到 data 中
int data_size ;
//进行 FFMPEG 音频重采样 ... 大块代码参考上一篇博客
//开始播放
if ( data_size > 0 ){
//通过播放器队列接口 , 将 PCM 数据加入到该队列缓冲区后 , 就会自动播放这段音频
// 注意 , 最后一个参数是样本字节数
(*bq)->Enqueue(bq, audioChannel->data, data_size);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
3 . 回调函数注册 :
// VIII . 注册回调函数
// 注册缓冲区队列的回调函数 , 每次播放完数据后 , 会自动回调该函数 // 传入参数 this , 就是 bqPlayerCallback 函数中的 context 参数 result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this); assert(SL_RESULT_SUCCESS == result); (void)result;