標籤:jni span asi proc rate thread include imp 結構體
/*
*
*這裡使用了transcode-1.1.7對wav檔案進行解碼,然後使用opensl es進行播放
*
*/
//用到的變數和結構體
WAV wav; //wav檔案指標SLObjectItf engineObject; //引擎對象SLEngineItf engineInterface; //引擎介面SLObjectItf outputMixObject; //混音器SLObjectItf audioPlayerObject; //播放器對象SLAndroidSimpleBufferQueueItf andioPlayerBufferQueueItf; //緩衝器隊列介面SLPlayItf audioPlayInterface; //播放介面unsigned char *buffer; //緩衝區size_t bufferSize; //緩衝區大小//上下文struct PlayerContext{ WAV wav; unsigned char *buffer; size_t bufferSize; PlayerContext(WAV wav, unsigned char *buffer, size_t bufferSize){ this->wav = wav; this->buffer = buffer; this->bufferSize = bufferSize; }};//實現對象void RealizeObject(SLObjectItf object){ //非非同步(阻塞) (*object)->Realize(object,SL_BOOLEAN_FALSE);}
具體實現流程:1.開啟檔案
WAV wav = OpenWaveFile(env,jFileName);
//開啟檔案WAV OpenWaveFile(JNIEnv *env,jstring jFileName){ const char *cFileName = env->GetStringUTFChars(jFileName,JNI_FALSE); WAVError err; WAV wav = wav_open(cFileName,WAV_READ,&err); LOGI("%d",wav_get_bitrate(wav)); env->ReleaseStringUTFChars(jFileName,cFileName); if(wav == 0){ LOGE("%s",wav_strerror(err)); } return wav;}
2.建立OpenSL ES引擎
//OpenSL ES在Android平台下預設是安全執行緒的,這樣設定是為了為了相容其他平台 SLEngineOption options[] = { {(SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE} }; slCreateEngine(&engineObject,ARRAY_LEN(engineObject),options,0,0,0); //沒有介面//執行個體化對象 //對象建立之後,處於未執行個體化狀態,對象雖然存在但未分配任何資源,使用前先執行個體化(使用完之後destroy) RealizeObject(engineObject);
3.擷取引擎介面
(*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&engineInterface);
4.建立輸出混音器
(*engineInterface)->CreateOutputMix(engineInterface,&outputMixObject,0,0,0); //沒有介面 //執行個體化混音器 RealizeObject(outputMixObject);
5.建立緩衝區儲存讀取到的音頻資料庫
//緩衝區的大小 bufferSize = wav_get_channels(wav) * wav_get_rate(wav) * wav_get_bits(wav); buffer = new unsigned char[bufferSize];
6.建立帶有緩衝區隊列的音頻播放器
CreateBufferQueueAudioPlayer(wav,engineInterface,outputMixObject,audioPlayerObject); //執行個體化音頻播放器 RealizeObject(audioPlayerObject);
CreateBufferQueueAudioPlayer.cpp
extern "C" {#include "wavlib.h"}#include <SLES/OpenSLES.h>#include <SLES/OpenSLES_Android.h>#include <android/log.h>#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))//建立音頻播放對象void CreateBufferQueueAudioPlayer( WAV wav, SLEngineItf engineEngine, SLObjectItf outputMixObject, SLObjectItf &audioPlayerObject){ // Android針對資料來源的簡單緩衝區隊列定位器 SLDataLocator_AndroidSimpleBufferQueue dataSourceLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // 定位器類型 1 // 緩衝區數 }; // PCM資料來源格式 SLDataFormat_PCM dataSourceFormat = { SL_DATAFORMAT_PCM, // 格式類型 wav_get_channels(wav), // 通道數 wav_get_rate(wav) * 1000, // 毫赫茲/秒的樣本數 wav_get_bits(wav), // 每個樣本的位元 wav_get_bits(wav), // 容器大小 SL_SPEAKER_FRONT_CENTER, // 通道屏蔽 SL_BYTEORDER_LITTLEENDIAN // 位元組順序 }; // 資料來源是含有PCM格式的簡單緩衝區隊列 SLDataSource dataSource = { &dataSourceLocator, // 資料定位器 &dataSourceFormat // 資料格式 }; // 針對資料接收器的輸出混合定位器 SLDataLocator_OutputMix dataSinkLocator = { SL_DATALOCATOR_OUTPUTMIX, // 定位器類型 outputMixObject // 輸出混合 }; // 資料定位器是一個輸出混合 SLDataSink dataSink = { &dataSinkLocator, // 定位器 0 // 格式 }; // 需要的介面 SLInterfaceID interfaceIds[] = { SL_IID_BUFFERQUEUE }; // 需要的介面,如果所需要的介面不要用,請求將失敗 SLboolean requiredInterfaces[] = { SL_BOOLEAN_TRUE // for SL_IID_BUFFERQUEUE }; // 建立音頻播放器對象 SLresult result = (*engineEngine)->CreateAudioPlayer( engineEngine, &audioPlayerObject, &dataSource, &dataSink, ARRAY_LEN(interfaceIds), interfaceIds, requiredInterfaces);}
7.獲得緩衝區隊列介面Buffer Queue Interface
//通過緩衝區隊列介面對緩衝區進行排序播放 (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_BUFFERQUEUE,&andioPlayerBufferQueueItf);
8.註冊音頻播放器回呼函數
//當播放器完成對前一個緩衝區隊列的播放時,回呼函數會被調用,然後我們又繼續讀取音頻資料,直到結束 //上下文,包裹參數方便再回呼函數中使用 PlayerContext *ctx = new PlayerContext(wav,buffer,bufferSize); (*andioPlayerBufferQueueItf)->RegisterCallback(andioPlayerBufferQueueItf,PlayerCallBack,ctx);
//回呼函數void PlayerCallBack(SLAndroidSimpleBufferQueueItf andioPlayerBufferQueue,void *context){ PlayerContext* ctx = (PlayerContext*)context; //讀取資料 ssize_t readSize = wav_read_data(ctx->wav,ctx->buffer,ctx->bufferSize); if(0 < readSize){ (*andioPlayerBufferQueue)->Enqueue(andioPlayerBufferQueue,ctx->buffer,readSize); }else{ //destroy context CloseWaveFile(ctx->wav); //關閉檔案 delete ctx->buffer; //釋放緩衝 }}
9.擷取Play Interface通過對SetPlayState函數來啟動播放音樂
//一旦播放器被設定為播放狀態,該音頻播放器開始等待緩衝區排隊就緒 (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_PLAY,&audioPlayInterface); //設定播放狀態 (*audioPlayInterface)->SetPlayState(audioPlayInterface,SL_PLAYSTATE_PLAYING);
10.開始,讓第一個緩衝區入隊
PlayerCallBack(andioPlayerBufferQueueItf,ctx);
完整代碼
#include "com_dongnaoedu_jasonaudioplayer_AudioPlayer.h"extern "C" {#include "wavlib.h"}#include <SLES/OpenSLES.h>#include <SLES/OpenSLES_Android.h>#include <android/log.h>#include "CreateBufferQueueAudioPlayer.cpp"#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"jason",FORMAT,##__VA_ARGS__);#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))WAV wav; //wav檔案指標SLObjectItf engineObject; //引擎對象SLEngineItf engineInterface; //引擎介面SLObjectItf outputMixObject; //混音器SLObjectItf audioPlayerObject; //播放器對象SLAndroidSimpleBufferQueueItf andioPlayerBufferQueueItf; //緩衝器隊列介面SLPlayItf audioPlayInterface; //播放介面unsigned char *buffer; //緩衝區size_t bufferSize; //緩衝區大小//上下文struct PlayerContext{ WAV wav; unsigned char *buffer; size_t bufferSize; PlayerContext(WAV wav, unsigned char *buffer, size_t bufferSize){ this->wav = wav; this->buffer = buffer; this->bufferSize = bufferSize; }};//開啟檔案WAV OpenWaveFile(JNIEnv *env,jstring jFileName){ const char *cFileName = env->GetStringUTFChars(jFileName,JNI_FALSE); WAVError err; WAV wav = wav_open(cFileName,WAV_READ,&err); LOGI("%d",wav_get_bitrate(wav)); env->ReleaseStringUTFChars(jFileName,cFileName); if(wav == 0){ LOGE("%s",wav_strerror(err)); } return wav;}//關閉檔案void CloseWaveFile(WAV wav){ wav_close(wav);}//實現對象void RealizeObject(SLObjectItf object){ //非非同步(阻塞) (*object)->Realize(object,SL_BOOLEAN_FALSE);}//回呼函數void PlayerCallBack(SLAndroidSimpleBufferQueueItf andioPlayerBufferQueue,void *context){ PlayerContext* ctx = (PlayerContext*)context; //讀取資料 ssize_t readSize = wav_read_data(ctx->wav,ctx->buffer,ctx->bufferSize); if(0 < readSize){ (*andioPlayerBufferQueue)->Enqueue(andioPlayerBufferQueue,ctx->buffer,readSize); }else{ //destroy context CloseWaveFile(ctx->wav); //關閉檔案 delete ctx->buffer; //釋放緩衝 }}JNIEXPORT void JNICALL Java_com_dongnaoedu_jasonaudioplayer_AudioPlayer_play (JNIEnv *env, jclass jthiz, jstring jFileName){ //1.開啟檔案 WAV wav = OpenWaveFile(env,jFileName); //2.建立OpenSL ES引擎 //OpenSL ES在Android平台下預設是安全執行緒的,這樣設定是為了為了相容其他平台 SLEngineOption options[] = { {(SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE} }; slCreateEngine(&engineObject,ARRAY_LEN(engineObject),options,0,0,0); //沒有介面 //執行個體化對象 //對象建立之後,處於未執行個體化狀態,對象雖然存在但未分配任何資源,使用前先執行個體化(使用完之後destroy) RealizeObject(engineObject); //3.擷取引擎介面 (*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&engineInterface); //4.建立輸出混音器 (*engineInterface)->CreateOutputMix(engineInterface,&outputMixObject,0,0,0); //沒有介面 //執行個體化混音器 RealizeObject(outputMixObject); //5.建立緩衝區儲存讀取到的音頻資料庫 //緩衝區的大小 bufferSize = wav_get_channels(wav) * wav_get_rate(wav) * wav_get_bits(wav); buffer = new unsigned char[bufferSize]; //6.建立帶有緩衝區隊列的音頻播放器 CreateBufferQueueAudioPlayer(wav,engineInterface,outputMixObject,audioPlayerObject); //執行個體化音頻播放器 RealizeObject(audioPlayerObject); //7.獲得緩衝區隊列介面Buffer Queue Interface //通過緩衝區隊列介面對緩衝區進行排序播放 (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_BUFFERQUEUE,&andioPlayerBufferQueueItf); //8.註冊音頻播放器回呼函數 //當播放器完成對前一個緩衝區隊列的播放時,回呼函數會被調用,然後我們又繼續讀取音頻資料,直到結束 //上下文,包裹參數方便再回呼函數中使用 PlayerContext *ctx = new PlayerContext(wav,buffer,bufferSize); (*andioPlayerBufferQueueItf)->RegisterCallback(andioPlayerBufferQueueItf,PlayerCallBack,ctx); //9.擷取Play Interface通過對SetPlayState函數來啟動播放音樂 //一旦播放器被設定為播放狀態,該音頻播放器開始等待緩衝區排隊就緒 (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_PLAY,&audioPlayInterface); //設定播放狀態 (*audioPlayInterface)->SetPlayState(audioPlayInterface,SL_PLAYSTATE_PLAYING); //10.開始,讓第一個緩衝區入隊 PlayerCallBack(andioPlayerBufferQueueItf,ctx); //關閉檔案 //CloseWaveFile(wav);}
Android音視頻學習第7章:使用OpenSL ES進行音頻解碼