日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android OpenSL ES 对象结构

發(fā)布時間:2024/4/11 Android 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android OpenSL ES 对象结构 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

OpenSL ES 是?Khronos Group 為嵌入式系統(tǒng)開發(fā)的調(diào)優(yōu)的免版權(quán)費、跨平臺、硬件加速的音頻 API 規(guī)范。Android 提供了這一 API 規(guī)范的 Android 專有實現(xiàn),并允許應(yīng)用程序通過 NDK 調(diào)用這些 API。Android 應(yīng)用可以借助這一 API,使用 C 或 C++ 實現(xiàn)高性能、低延遲的音頻。OpenSL ES? 標(biāo)準(zhǔn)與 Android Java 框架中的?MediaPlayer?和?MediaRecorderAPI 提供類似的音頻功能。OpenSL ES 提供 C 語言接口和 C++ 綁定,使我們可以從使用任意一種語言編寫的代碼中調(diào)用 API。

OpenSL ES 的使用

與在 Android 應(yīng)用中使用其它的第三方 C/C++ 庫類似,我們需要把 OpenSL ES 添加進(jìn)我們的應(yīng)用中,并編寫調(diào)用 OpenSL ES API 的代碼。把 OpenSL ES 添加進(jìn)我們的應(yīng)用,具體而言指添加相關(guān)的頭文件,并配置我們的動態(tài)鏈接庫的編譯選項以正確的鏈接 OpenSL ES 庫。

標(biāo)準(zhǔn)的核心 OpenSL ES 頭文件有 <SLES/OpenSLES.h> 和 <SLES/OpenSLES_Platform.h>,而 <SLES/OpenSLES_Android.h> 和 <SLES/OpenSLES_AndroidConfiguration.h> 中則提供了其他 Android 專用功能。

添加核心 OpenSL ES 功能集的話,包含 <SLES/OpenSLES.h> 頭文件即可,這個頭文件包含了 <SLES/OpenSLES_Platform.h>。添加 OpenSL ES?Android 擴(kuò)展,則可以添加?<SLES/OpenSLES_Android.h> 頭文件,這個頭文件包含如下兩個頭文件:

#include <SLES/OpenSLES_AndroidConfiguration.h> #include <SLES/OpenSLES_AndroidMetadata.h>

配置編譯選項引入 OpenSL ES 庫使得鏈接器能夠正確鏈接這個庫的方法,因我們使用的編譯配置系統(tǒng)的不同而不同。如果我們使用 Android.mk 配置編譯,需要為 Android.mk
文件添加如下行:

LOCAL_LDLIBS += -lOpenSLES

如果使用 CMake 的話,target_link_libraries 需要加上 OpenSLES,如:

target_link_libraries(echoPRIVATEOpenSLES )

Google 的 android ndk-samples 中有許多 NDK 相關(guān)的示例代碼,其中包含有 OpenSL ES 相關(guān)的示例代碼 audio-echo?和?native-audio。

這里通過一段基于 audio-echo 的實現(xiàn)了 Android 平臺的耳返功能的代碼看一下 OpenSL ES API 的基本使用方法。在這段示例代碼中,啟動聲音錄制設(shè)備錄制聲音,錄得的聲音數(shù)據(jù)被放進(jìn)一個中間緩沖區(qū),中間緩沖區(qū)中的數(shù)據(jù)隨后又被送進(jìn)播放設(shè)備播放出來。

OpenSL ES 是一套 C 的面向?qū)ο?API,它的基本使用方法如下:

  • 創(chuàng)建 Engine 對象
  • result = slCreateEngine(&engine.slEngineObj_, 0, NULL, 0, NULL, NULL);SLASSERT(result);result =(*engine.slEngineObj_)->Realize(engine.slEngineObj_, SL_BOOLEAN_FALSE);SLASSERT(result);result = (*engine.slEngineObj_)->GetInterface(engine.slEngineObj_, SL_IID_ENGINE,&engine.slEngineItf_);SLASSERT(result);

    先由 OpenSL ES API 的入口函數(shù) slCreateEngine() 創(chuàng)建類型為 SLObjectItf 的 slEngineObj_,然后借助于 slEngineObj_ 獲得類型為 SLEngineItf 的 Engine 對象 slEngineItf_。

  • 創(chuàng)建 Recorder
  • AudioRecorder::AudioRecorder(SampleFormat *sampleFormat, SLEngineItf slEngine): freeQueue_(nullptr),recQueue_(nullptr),devShadowQueue_(nullptr),callback_(nullptr) {SLresult result;sampleInfo_ = *sampleFormat;SLAndroidDataFormat_PCM_EX format_pcm;ConvertToSLSampleFormat(&format_pcm, &sampleInfo_);// configure src.audio sourceSLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,SL_IODEVICE_AUDIOINPUT,SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};SLDataSource audioSrc = {&loc_dev, NULL};// configure src.audio sinkSLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, DEVICE_SHADOW_BUFFER_QUEUE_LEN};SLDataSink audioSnk = {&loc_bq, &format_pcm};// create src.audio recorder// (requires the RECORD_AUDIO permission)const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE,SL_IID_ANDROIDCONFIGURATION};const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};result = (*slEngine)->CreateAudioRecorder(slEngine, &recObjectItf_, &audioSrc, &audioSnk,sizeof(id) / sizeof(id[0]), id, req);SLASSERT(result);// Configure the voice recognition preset which has no// signal processing for lower latency.SLAndroidConfigurationItf inputConfig;result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_ANDROIDCONFIGURATION,&inputConfig);if (SL_RESULT_SUCCESS == result) {SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;(*inputConfig)->SetConfiguration(inputConfig, SL_ANDROID_KEY_RECORDING_PRESET,&presetValue, sizeof(SLuint32));}result = (*recObjectItf_)->Realize(recObjectItf_, SL_BOOLEAN_FALSE);SLASSERT(result);result =(*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_RECORD, &recItf_);SLASSERT(result);result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,&recBufQueueItf_);SLASSERT(result);result = (*recBufQueueItf_)->RegisterCallback(recBufQueueItf_, bqRecorderCallback, this);SLASSERT(result);devShadowQueue_ = new AudioQueue(DEVICE_SHADOW_BUFFER_QUEUE_LEN);assert(devShadowQueue_); #ifdef ENABLE_LOGstd::string name = "rec";recLog_ = new AndroidLog(name); #endif }

    創(chuàng)建 Recorder 的過程如下:

    • 為 Recorder 定義流水線,即音頻數(shù)據(jù)流動的方式 Audio Sink 和 Audio Source。
    • 通過 SLEngineItf 的 CreateAudioRecorder() 接口創(chuàng)建類型為 SLObjectItf 的 Recorder 對象。
    • 配置 Recorder。
    • 獲得類型為 SLRecordItf 的 recItf_。

    上面這段代碼創(chuàng)建的 recording pipeline 大體如下:

    配置 OpenSL ES recorder 從錄音設(shè)備拿到音頻數(shù)據(jù),放入緩沖區(qū),這些數(shù)據(jù)再由緩沖區(qū)對象通過回調(diào)傳給外界。Android OpenSL ES 中用 SLAndroidSimpleBufferQueueItf 對象描述緩沖區(qū),這個緩沖區(qū)本身不包含用于存放音頻數(shù)據(jù)的內(nèi)存,而是需要外部分配內(nèi)存,并將分配好的內(nèi)存給到緩沖區(qū)對象。

  • 啟動和停止 Recorder
  • 上個步驟創(chuàng)建的類型為 SLRecordItf 的 recItf_ 對象用于控制 recorder 的狀態(tài),包括 recorder 的啟動和停止:

    SLboolean AudioRecorder::Start(void) {if (!freeQueue_ || !recQueue_ || !devShadowQueue_) {LOGE("====NULL poiter to Start(%p, %p, %p)", freeQueue_, recQueue_,devShadowQueue_);return SL_BOOLEAN_FALSE;}audioBufCount = 0;SLresult result;// in case already recording, stop recording and clear buffer queueresult = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);SLASSERT(result);result = (*recBufQueueItf_)->Clear(recBufQueueItf_);SLASSERT(result);for (int i = 0; i < RECORD_DEVICE_KICKSTART_BUF_COUNT; i++) {sample_buf *buf = NULL;if (!freeQueue_->front(&buf)) {LOGE("=====OutOfFreeBuffers @ startingRecording @ (%d)", i);break;}freeQueue_->pop();assert(buf->buf_ && buf->cap_ && !buf->size_);result = (*recBufQueueItf_)->Enqueue(recBufQueueItf_, buf->buf_, buf->cap_);SLASSERT(result);devShadowQueue_->push(buf);}result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_RECORDING);SLASSERT(result);return (result == SL_RESULT_SUCCESS ? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE); }SLboolean AudioRecorder::Stop(void) {// in case already recording, stop recording and clear buffer queueSLuint32 curState;SLresult result = (*recItf_)->GetRecordState(recItf_, &curState);SLASSERT(result);if (curState == SL_RECORDSTATE_STOPPED) {return SL_BOOLEAN_TRUE;}result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);SLASSERT(result);result = (*recBufQueueItf_)->Clear(recBufQueueItf_);SLASSERT(result);#ifdef ENABLE_LOGrecLog_->flush(); #endifreturn SL_BOOLEAN_TRUE; }

    OpenSL ES 沒有為啟動和停止 recorder 創(chuàng)建專門的接口,而是通過修改 recorder 的狀態(tài)的接口實現(xiàn)的。

  • 處理錄制數(shù)據(jù)
  • 可以給 SLAndroidSimpleBufferQueueItf 對象配置多塊分配好的內(nèi)存緩沖區(qū),當(dāng)一塊緩沖區(qū)中填滿了錄制數(shù)據(jù)時,注冊的回調(diào)被調(diào)用。當(dāng)回調(diào)被調(diào)用時,可以這樣來處理數(shù)據(jù):

    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 總是在緩沖區(qū)滿時調(diào)用回調(diào),則配置的緩沖區(qū)大小對錄制的延遲及回調(diào)調(diào)用的間隔有著重大影響。

  • 銷毀 Recorder
  • 錄制結(jié)束時,銷毀 recorder:

    AudioRecorder::~AudioRecorder() {// destroy src.audio recorder object, and invalidate all associated interfacesif (recObjectItf_ != NULL) {(*recObjectItf_)->Destroy(recObjectItf_);}if (devShadowQueue_) {sample_buf *buf = NULL;while (devShadowQueue_->front(&buf)) {devShadowQueue_->pop();freeQueue_->push(buf);}delete (devShadowQueue_);} #ifdef ENABLE_LOGif (recLog_) {delete recLog_;} #endif }
  • 創(chuàng)建 Player
  • AudioPlayer::AudioPlayer(SampleFormat *sampleFormat, SLEngineItf slEngine): freeQueue_(nullptr),playQueue_(nullptr),devShadowQueue_(nullptr),callback_(nullptr) {SLresult result;assert(sampleFormat);sampleInfo_ = *sampleFormat;result = (*slEngine)->CreateOutputMix(slEngine, &outputMixObjectItf_, 0, NULL, NULL);SLASSERT(result);// realize the output mixresult =(*outputMixObjectItf_)->Realize(outputMixObjectItf_, SL_BOOLEAN_FALSE);SLASSERT(result);// configure src.audio sourceSLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, DEVICE_SHADOW_BUFFER_QUEUE_LEN};SLAndroidDataFormat_PCM_EX format_pcm;ConvertToSLSampleFormat(&format_pcm, &sampleInfo_);SLDataSource audioSrc = {&loc_bufq, &format_pcm};// configure src.audio sinkSLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX,outputMixObjectItf_};SLDataSink audioSnk = {&loc_outmix, NULL};/** create fast path src.audio player: SL_IID_BUFFERQUEUE and SL_IID_VOLUME* and other non-signal processing interfaces are ok.*/SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};result = (*slEngine)->CreateAudioPlayer(slEngine, &playerObjectItf_, &audioSrc, &audioSnk,sizeof(ids) / sizeof(ids[0]), ids, req);SLASSERT(result);// realize the playerresult = (*playerObjectItf_)->Realize(playerObjectItf_, SL_BOOLEAN_FALSE);SLASSERT(result);// get the play interfaceresult = (*playerObjectItf_)->GetInterface(playerObjectItf_, SL_IID_PLAY, &playItf_);SLASSERT(result);// get the buffer queue interfaceresult = (*playerObjectItf_)->GetInterface(playerObjectItf_, SL_IID_BUFFERQUEUE,&playBufferQueueItf_);SLASSERT(result);// register callback on the buffer queueresult = (*playBufferQueueItf_)->RegisterCallback(playBufferQueueItf_, bqPlayerCallback, this);SLASSERT(result);result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);SLASSERT(result);// create an empty queue to track deviceQueuedevShadowQueue_ = new AudioQueue(DEVICE_SHADOW_BUFFER_QUEUE_LEN);assert(devShadowQueue_);silentBuf_.cap_ = (format_pcm.containerSize >> 3) * format_pcm.numChannels *sampleInfo_.framesPerBuf_;silentBuf_.buf_ = new uint8_t[silentBuf_.cap_];memset(silentBuf_.buf_, 0, silentBuf_.cap_);silentBuf_.size_ = silentBuf_.cap_;#ifdef ENABLE_LOGstd::string name = "play";logFile_ = new AndroidLog(name); #endif }

    創(chuàng)建 Player 的過程與創(chuàng)建 Recorder 的過程類似,同樣要定義流水線,只是流水線的樣子有差別。Player 的流水線如下面這樣:

    這個過程最終獲得類型為 SLPlayItf 的 playItf_ 來控制播放。

  • 播放的啟動和停止
  • SLresult AudioPlayer::Start(void) {SLuint32 state;SLresult result = (*playItf_)->GetPlayState(playItf_, &state);if (result != SL_RESULT_SUCCESS) {return SL_BOOLEAN_FALSE;}if (state == SL_PLAYSTATE_PLAYING) {return SL_BOOLEAN_TRUE;}result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);SLASSERT(result);result =(*playBufferQueueItf_)->Enqueue(playBufferQueueItf_, silentBuf_.buf_, silentBuf_.size_);SLASSERT(result);devShadowQueue_->push(&silentBuf_);result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_PLAYING);SLASSERT(result);return SL_BOOLEAN_TRUE; }void AudioPlayer::Stop(void) {SLuint32 state;SLresult result = (*playItf_)->GetPlayState(playItf_, &state);SLASSERT(result);if (state == SL_PLAYSTATE_STOPPED) return;std::lock_guard<std::mutex> lock(stopMutex_);result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);SLASSERT(result);(*playBufferQueueItf_)->Clear(playBufferQueueItf_);#ifdef ENABLE_LOGif (logFile_) {delete logFile_;logFile_ = nullptr;} #endif }

    這個過程同樣與 recorder 的對應(yīng)過程類似,通過改變狀態(tài)來控制播放的啟動與停止。

  • 播放回調(diào)的處理
  • 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_);} }

    在錄制回調(diào)中,將錄制的數(shù)據(jù)傳給外界,在播放的回調(diào)中,則將外部數(shù)據(jù)拷貝給 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_; }

    播放結(jié)束之后,需要銷毀相關(guān)的對象。

  • 銷毀 engine
  • JNIEXPORT void JNICALL MainActivity_deleteSLEngine(JNIEnv *env, jclass type) {LOGI("MainActivity_deleteSLEngine");delete engine.recBufQueue_;delete engine.freeBufQueue_;releaseSampleBufs(engine.bufs_, engine.bufCount_);if (engine.slEngineObj_ != NULL) {(*engine.slEngineObj_)->Destroy(engine.slEngineObj_);engine.slEngineObj_ = NULL;engine.slEngineItf_ = NULL;}if (engine.delayEffect_) {delete engine.delayEffect_;engine.delayEffect_ = nullptr;} }

    OpenSL ES 的主要對象結(jié)構(gòu)

    OpenSL ES 的對象可以大體總結(jié)如下:

    上面的示例代碼只是展示了 OpenSL ES 可以搭建的一種流水線,對于 OpenSL ES API 的其它使用方式可以參考官方的文檔。

    參考文檔:

    OpenSL ES
    OpenSL ES 使用入門
    OpenSL ES for Android
    OpenSL ES Android 擴(kuò)展
    OpenSL ES 編程說明

    總結(jié)

    以上是生活随笔為你收集整理的Android OpenSL ES 对象结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。