OpenSL ES 是?Khronos Group 為嵌入式系統開發的調優的免版權費、跨平臺、硬件加速的音頻 API 規范。Android 提供了這一 API 規范的 Android 專有實現,并允許應用程序通過 NDK 調用這些 API。Android 應用可以借助這一 API,使用 C 或 C++ 實現高性能、低延遲的音頻。OpenSL ES? 標準與 Android Java 框架中的?MediaPlayer?和?MediaRecorderAPI 提供類似的音頻功能。OpenSL ES 提供 C 語言接口和 C++ 綁定,使我們可以從使用任意一種語言編寫的代碼中調用 API。
OpenSL ES 的使用
與在 Android 應用中使用其它的第三方 C/C++ 庫類似,我們需要把 OpenSL ES 添加進我們的應用中,并編寫調用 OpenSL ES API 的代碼。把 OpenSL ES 添加進我們的應用,具體而言指添加相關的頭文件,并配置我們的動態鏈接庫的編譯選項以正確的鏈接 OpenSL ES 庫。
配置 OpenSL ES recorder 從錄音設備拿到音頻數據,放入緩沖區,這些數據再由緩沖區對象通過回調傳給外界。Android OpenSL ES 中用 SLAndroidSimpleBufferQueueItf 對象描述緩沖區,這個緩沖區本身不包含用于存放音頻數據的內存,而是需要外部分配內存,并將分配好的內存給到緩沖區對象。
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *rec) {(static_cast<AudioRecorder *>(rec))->ProcessSLCallback(bq);
}void AudioRecorder::ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq) {
#ifdef ENABLE_LOGrecLog_->logTime();
#endifassert(bq == recBufQueueItf_);sample_buf *dataBuf = NULL;devShadowQueue_->front(&dataBuf);devShadowQueue_->pop();dataBuf->size_ = dataBuf->cap_; // device only calls us when it is really// fullcallback_(ctx_, ENGINE_SERVICE_MSG_RECORDED_AUDIO_AVAILABLE, dataBuf);recQueue_->push(dataBuf);sample_buf *freeBuf;while (freeQueue_->front(&freeBuf) && devShadowQueue_->push(freeBuf)) {freeQueue_->pop();SLresult result = (*bq)->Enqueue(bq, freeBuf->buf_, freeBuf->cap_);SLASSERT(result);}++audioBufCount;// should leave the device to sleep to save power if no buffersif (devShadowQueue_->size() == 0) {LOGI("devShadowQueue size is 0, stop recording");(*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);}
}
由于 OpenSL ES 總是在緩沖區滿時調用回調,則配置的緩沖區大小對錄制的延遲及回調調用的間隔有著重大影響。
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *ctx) {(static_cast<AudioPlayer *>(ctx))->ProcessSLCallback(bq);
}
void AudioPlayer::ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq) {LOGI("AudioPlayer::ProcessSLCallback");
#ifdef ENABLE_LOGlogFile_->logTime();
#endifstd::lock_guard<std::mutex> lock(stopMutex_);// retrieve the finished device buf and put onto the free queue// so recorder could re-use itsample_buf *buf;if (!devShadowQueue_->front(&buf)) {/** This should not happen: we got a callback,* but we have no buffer in deviceShadowedQueue* we lost buffers this way...(ERROR)*/if (callback_) {uint32_t count;callback_(ctx_, ENGINE_SERVICE_MSG_RETRIEVE_DUMP_BUFS, &count);}return;}devShadowQueue_->pop();if (buf != &silentBuf_) {buf->size_ = 0;freeQueue_->push(buf);if (!playQueue_->front(&buf)) {
#ifdef ENABLE_LOGlogFile_->log("%s", "====Warning: running out of the Audio buffers");
#endifreturn;}devShadowQueue_->push(buf);(*bq)->Enqueue(bq, buf->buf_, buf->size_);playQueue_->pop();return;}if (playQueue_->size() < PLAY_KICKSTART_BUFFER_COUNT) {(*bq)->Enqueue(bq, buf->buf_, buf->size_);devShadowQueue_->push(&silentBuf_);return;}assert(PLAY_KICKSTART_BUFFER_COUNT <=(DEVICE_SHADOW_BUFFER_QUEUE_LEN - devShadowQueue_->size()));for (int32_t idx = 0; idx < PLAY_KICKSTART_BUFFER_COUNT; idx++) {playQueue_->front(&buf);playQueue_->pop();devShadowQueue_->push(buf);(*bq)->Enqueue(bq, buf->buf_, buf->size_);}
}
在錄制回調中,將錄制的數據傳給外界,在播放的回調中,則將外部數據拷貝給 OpenSL ES 使用。
銷毀 Player
AudioPlayer::~AudioPlayer() {std::lock_guard<std::mutex> lock(stopMutex_);// destroy buffer queue src.audio player object, and invalidate all associated// interfacesif (playerObjectItf_ != NULL) {(*playerObjectItf_)->Destroy(playerObjectItf_);}// Consume all non-completed src.audio bufferssample_buf *buf = NULL;while (devShadowQueue_->front(&buf)) {buf->size_ = 0;devShadowQueue_->pop();if(buf != &silentBuf_) {freeQueue_->push(buf);}}delete devShadowQueue_;while (playQueue_->front(&buf)) {buf->size_ = 0;playQueue_->pop();freeQueue_->push(buf);}// destroy output mix object, and invalidate all associated interfacesif (outputMixObjectItf_) {(*outputMixObjectItf_)->Destroy(outputMixObjectItf_);}delete[] silentBuf_.buf_;
}