OpenSLES:(Open Sound Library for Embedded Systems)
OpenSLES是跨平臺、針對嵌入式系統精心優化的硬件音頻加速API。使用OpenSLES進行音頻播放的好處是可以不依賴第三方。它爲嵌入式移動多媒體設備上的本地應用程序開發者提供標準化, 高性能,低響應時間的音頻功能實現方法,並實現軟/硬件音頻性能的直接跨平臺部署,降低執行難度。
今天我們就通過OpenSLES代碼來實現播放pcm格式的音頻文件,後期會將相關代碼整合到ffmpeg播放器上,實現音視頻同步。下邊是OpenSLES播放音頻的簡要流程(圖片來自網上)
接下來進入代碼階段
首先使用OpenSLES需要將這個庫鏈接到我們的so中,在CMakeLists.txt腳本中添加
target_link_libraries(
......
#播放音頻
OpenSLES )
實現流程
1.創建引擎
slCreateEngine(),第一個參數是要創建的引擎對象,是一個SLObjectItf類型,Object是一個資源的抽象集合,可以通過它獲取各種資源,所有的Object在OpenSL裏面我們拿到的都是一個SLObjectItf,返回值是SLresult類型,如果成功則返回SL_RESULT_SUCCESS,其他參數都傳0即可,我們務求最精簡的實現功能
SLObjectItf *pEngine,
SLuint32 numOptions,
const SLEngineOption *pEngineOptions,
SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds,
const SLboolean * pInterfaceRequired
2.引擎對象創建後實例化,創建出來之後必須先調用Realize方法做初始化。在不需要使用的時候調用Destroy方法釋放資源
(*slObjectItf)->Realize(),實例化成功則返回SL_RESULT_SUCCESS
SLObjectItf self, //要被實例化的引擎對象本身
SLboolean async //傳SL_BOOLEAN_FALSE
3.引擎實例化之後從引擎對象獲取接口
Interface則是方法的集合,例如SLRecordItf裏面包含了和錄音相關的方法,SLPlayItf包含了和播放相關的方法。我們功能都是通過調用Interfaces的方法去實現的,個Object裏面可能包含了多個Interface,所以GetInterface方法有個SLInterfaceID參數來指定到的需要獲取Object裏面的那個Interface。調用這個方法會獲取到SLEngineItf,SLEngineItf是OpenSL裏面最重要的一個Interface,我們可以通過它去創建各種Object,例如播放器、錄音器、混音器的Object,然後在用這些Object去獲取各種Interface去實現各種功能
(*slObjectItf)->GetInterface() 執行會後會獲取到SLEngineItf slEngineItf對象
SLObjectItf self, //實例化後的引擎對象
const SLInterfaceID iid, //SL_IID_ENGINE
void * pInterface //輸出的接口對象指針
4.創建混音器
混音器是從創建出的引擎接口中創建的
(*slEngineItf)->CreateOutputMix(),輸出的混音器同樣是SLObjectItf mixObjectItf類型
SLEngineItf self, //引擎接口
SLObjectItf * pMix, //輸出的混音器
SLuint32 numInterfaces,//傳0
const SLInterfaceID * pInterfaceIds,//傳0
const SLboolean * pInterfaceRequired //傳0
5.混音器對象創建後開始實例化,同引擎實例化
(*mixObjectItf)->Realize()
SLObjectItf self, //要被實例化的混音器對象本身
SLboolean async //傳SL_BOOLEAN_FALSE
6.創建播放器
(*slEngineItf)->CreateAudioPlayer(),播放器是從引擎對象創建出來的
SLEngineItf self, //引擎對象本身
SLObjectItf * pPlayer, //輸出的播放器對象,同樣是SLObjectItf類型
SLDataSource *pAudioSrc,//數據的來源
SLDataSink *pAudioSnk, //數據的去處,和SLDataSource是相對的
SLuint32 numInterfaces,//與下面的SLInterfaceID和SLboolean配合使用,用於標記SLInterfaceID數組和SLboolean的大小
const SLInterfaceID * pInterfaceIds, //這裏需要傳入一個數組,指定創建的播放器會包含哪些Interface
const SLboolean * pInterfaceRequired //這裏也是一個數組,用來標記每個需要包含的Interface
7.初始化播放器
(*slPlayerItf)->Realize() 得到SLObjectItf slPlayerItf = NULL;
SLObjectItf self, //要被實例化的播放器對象本身
SLboolean async //傳SL_BOOLEAN_FALSE
8.獲取播放器接口
(*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_PLAY, &slPlayItf);
得到播放器接口SLPlayItf slPlayItf
SLObjectItf self, //實例化後的播放器對象
const SLInterfaceID iid, //SL_IID_ENGINE
void * pInterface //輸出的接口對象指針
9,獲取播放隊列接口
(*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_BUFFERQUEUE, &pcmQueue)
SLObjectItf self,
const SLInterfaceID iid, //SL_IID_ENGINE
void * pInterface //輸出的接口對象指針
10.給播放隊列設置回調函數
(*pcmQueue)->RegisterCallback(pcmQueue, CallBack, 0);
開始播放後會不斷的回調這個函數將音頻數據壓入隊列
void CallBack(SLAndroidSimpleBufferQueueItf bf, void *contex) {
LOGI("CallBack ....");
static FILE *fp = NULL;
static char *buf = NULL;
if (!buf) {
buf = new char[1024 * 1024];
}
if (!fp) {
fp = fopen("/sdcard/test.pcm", "rb");
}
if (!fp)return;
if (feof(fp) == 0) {
int len = fread(buf, 1, 1024, fp);
if (len > 0)
(*bf)->Enqueue(bf, buf, len);
}
}
11.設置播放狀態爲播放中
(*slPlayItf)->SetPlayState(slPlayItf, SL_PLAYSTATE_PLAYING);
12.開始播放
(*pcmQueue)->Enqueue(pcmQueue, "", 1);
完整代碼
void CallBack(SLAndroidSimpleBufferQueueItf bf, void *contex) {
LOGI("CallBack ....");
static FILE *fp = NULL;
static char *buf = NULL;
if (!buf) {
buf = new char[1024 * 1024];
}
if (!fp) {
fp = fopen("/sdcard/test.pcm", "rb");
}
if (!fp)return;
if (feof(fp) == 0) {
int len = fread(buf, 1, 1024, fp);
if (len > 0)
(*bf)->Enqueue(bf, buf, len);
}
}
/**
* 播放音頻
*/
extern "C"
JNIEXPORT void JNICALL
Java_com_rzm_ffmpegplayer_FFmpegPlayer_playAudio(JNIEnv *env, jobject instance, jstring url_) {
const char *url = env->GetStringUTFChars(url_, 0);
/*********** 1 創建引擎 獲取SLEngineItf***************/
SLObjectItf slObjectItf;
SLEngineItf slEngineItf;
//SLresult是unsigned int 類型
SLresult result;
result = slCreateEngine(&slObjectItf, 0, 0, 0, 0, 0);
if (result != SL_RESULT_SUCCESS)
return;
//SLObjectItf本身是一個指針,*slObjectItf得到的是他的對象
result = (*slObjectItf)->Realize(slObjectItf, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
return;
result = (*slObjectItf)->GetInterface(slObjectItf, SL_IID_ENGINE, &slEngineItf);
if (result != SL_RESULT_SUCCESS)
return;
if (slEngineItf) {
LOGI("get SLEngineItf success");
} else {
LOGI("get SLEngineItf failed");
}
/*********** 1 創建引擎 ***************/
/*********** 2 創建混音器 ***************/
SLObjectItf mixObjectItf = NULL;
result = (*slEngineItf)->CreateOutputMix(slEngineItf, &mixObjectItf, 0, 0, 0);
if (result != SL_RESULT_SUCCESS) {
LOGE("CreateOutputMix failed");
return;
} else {
LOGE("CreateOutputMix success");
}
//實例化混音器
result = (*mixObjectItf)->Realize(mixObjectItf, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
LOGE("mixer init failed");
} else {
LOGI("mixer init success");
}
/*********** 2 創建混音器 ***************/
/*********** 3 配置音頻信息 ***************/
SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, mixObjectItf};
SLDataSink slDataSink = {&outputMix, 0};
//緩衝隊列
SLDataLocator_AndroidSimpleBufferQueue queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 10};
//音頻格式
SLDataFormat_PCM pcmFormat = {
SL_DATAFORMAT_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
};
SLDataSource slDataSource = {&queue, &pcmFormat};
/*********** 3 配置音頻信息 ***************/
/************* 4 創建播放器 ****************/
const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
const SLboolean req[] = {SL_BOOLEAN_TRUE};
SLObjectItf slPlayerItf = NULL;
SLPlayItf slPlayItf;
SLAndroidSimpleBufferQueueItf pcmQueue = NULL;
result = (*slEngineItf)->CreateAudioPlayer(slEngineItf, &slPlayerItf, &slDataSource,
&slDataSink, sizeof(ids) / sizeof(SLInterfaceID),
ids, req);
if (result != SL_RESULT_SUCCESS) {
LOGE("create audio player failed");
} else {
LOGE("create audio player success");
}
//初始化播放器
result = (*slPlayerItf)->Realize(slPlayerItf, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
LOGE("audio player init failed");
} else {
LOGE("audio player init success");
}
//獲取player接口
result = (*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_PLAY, &slPlayItf);
if (result != SL_RESULT_SUCCESS) {
LOGE("player get SL_IID_PLAY failed");
} else {
LOGI("player get SL_IID_PLAY success");
}
//獲取播放隊列接口
result = (*slPlayerItf)->GetInterface(slPlayerItf, SL_IID_BUFFERQUEUE, &pcmQueue);
if (result != SL_RESULT_SUCCESS) {
LOGE("player get SL_IID_BUFFERQUEUE failed");
} else {
LOGI("player get SL_IID_BUFFERQUEUE success");
}
/************* 4 創建播放器 ****************/
//設置回調函數
(*pcmQueue)->RegisterCallback(pcmQueue, CallBack, 0);
//設置播放狀態
(*slPlayItf)->SetPlayState(slPlayItf, SL_PLAYSTATE_PLAYING);
//啓動隊列
(*pcmQueue)->Enqueue(pcmQueue, "", 1);
env->ReleaseStringUTFChars(url_, url);
}