目錄
播放流程和條件
Opengl SLObjectItf 對象建立的四闆斧
播放pcm的流程
播放流程和條件
android自帶的openSL庫,可用來解碼音頻,也可以來播放音頻,以及錄音。要在jni層調用:
1、cmakeList 中target_link_libraries 内引入庫:OpenSLES
2、引入頭檔案:
#include "SLES/OpenSLES.h"
#include "SLES/OpenSLES_Android.h"
播放流程

播放隊列
隻要往播放隊列中壓入資料,就會播放。這裡采用的是回調函數,當取出buf播放完畢就會清理buf,讓隊列空出,這時候就push資料叫你去,進而播放。
OPengl SLObjectItf 對象建立的四闆斧
一般對象建立完畢後,用到的就是 SLObjectItf的對象 和 interface接口功能對象
1、定義 SLObjectItf
Opensl機構體SLObjectItf可以定義大部分結構體,此篇文章結構體對象都是用它初始化的。結構體 SLObjectItf 類似于java的Object類,屬于一個基結構體。音頻的大多數結構體都繼承于它。
typedef const struct SLObjectItf_ * const * SLObjectItf;
從結構以定義看出,它是一個結構以指針,是以,定義一個SLObjectItf 變量,其實是定義一個 SLObjectItf的指針。
2、建立 slCreate
因為SLObjectItf是指針對象,為了更好管理記憶體,會将SLObjectItf位址傳入opensl中,來建立。下面為建立Engine引擎的例子:
SLObjectItf engineSL = NULL;
re = slCreateEngine(&engineSL,0,0,0,0,0);
- 1、對象空間就會在 opensl内部建立,友善利用opensl内部釋放
- 2、後面參數為支援建立的接口數量,和注冊接口,友善後面getInterface獲得接口功能。不注冊後面會後去失敗。
3、執行個體化 Realize
SLresult (*Realize) (
SLObjectItf self,
SLboolean async
);// 第二個參數,是否等待建立好再傳回
這個方法會将對象的一些參數配置設定好。
4、獲得功能接口 GetInterface
SLresult (*GetInterface) (
SLObjectItf self,
const SLInterfaceID iid,
void * pInterface
);
參數:
SLObjectItf self 擷取結構對象
const SLInterfaceID iid, 擷取的結構功能id
void * pInterface 存儲接口功能的對象,即接口對象。
下面為建立一個音頻引擎的方法例子,大家就可以看出來:
//定義
static SLObjectItf engineSL = NULL;
SLEngineItf CreateSL(){
SLresult re;
SLEngineItf en;
// 建立
re = slCreateEngine(&engineSL,0,0,0,0,0);
if(re != SL_RESULT_SUCCESS)
return NULL;
// 執行個體化
re= (*engineSL)->Realize(engineSL, SL_BOOLEAN_FALSE);// 第二個參數,是否等待建立好再傳回
if(re != SL_RESULT_SUCCESS) return NULL;
// 擷取引擎接口,并存入en功能接口對象
re = (*engineSL)->GetInterface(engineSL,SL_IID_ENGINE, &en);
if(re != SL_RESULT_SUCCESS) return NULL;
return en;
}
播放pcm的流程
1、建立播放引擎
上面的代碼已經給出了,建立音頻播放引擎對象,和獲得 播放引擎接口對象。這裡介紹下函數
SL_API SLresult SLAPIENTRY slCreateEngine(
SLObjectItf *pEngine,
SLuint32 numOptions,
const SLEngineOption *pEngineOptions,
SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds,
const SLboolean * pInterfaceRequired
);
參數:
SLObjectItf *pEngine, 對象
SLuint32 numOptions, 選擇項數量
const SLEngineOption *pEngineOptions, 具體的選擇項
SLuint32 numInterfaces, 支援接口的數量
const SLInterfaceID *pInterfaceIds, 支援的接口
const SLboolean * pInterfaceRequired 接口是關閉還是打開
2、建立輸出裝置
SLEngineItf eng = CreateSL();
// 建立 混音器
SLObjectItf mix = NULL;
re = (*eng)->CreateOutputMix(eng,&mix,0,0,0);// 後面配置項,做混音特效
if(re != SL_RESULT_SUCCESS){
LOGE("CreateOutputMix failed");
}
//執行個體化 mix
re = (*mix)->Realize(mix, SL_BOOLEAN_FALSE);
if(re != SL_RESULT_SUCCESS){
LOGE("mix Realize failed");
}
// 建立一個輸出接口,給播放器調用
SLDataLocator_OutputMix outMix = {SL_DATALOCATOR_OUTPUTMIX, mix};
SLDataSink audioSink= {&outMix,0};
從代碼可以看出,SLObjectItf 對象擷取功能接口用getInterface,而其他對象并不是,這裡目的:
- 建立混音器 mix
- 建立一個混音器輸出對象audioSink, 而混音的輸出對象outmix,就存在audioSink中初始化的。
3、配置pcm音頻格式資訊
// 配置音頻資訊
//緩沖隊列
SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE};
//音頻格式配置
SLDataFormat_PCM pcm={
SL_DATAFORMAT_PCM,
2,// 聲道數
SL_SAMPLINGRATE_44_1,//采用率44100
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN// 位元組序,小端
};
// 建構結構體給 播放器使用
SLDataSource ds={&que, &pcm};
這裡由代碼可以看出,
- 建立了一個播放的音頻的隊列que。
- 建立配置音頻pcm格式資訊的對象pcm。
- 将播放隊列和音頻格式元件一個結構體ds,友善給播放器使用。
4、建立播放器
// 建立播放器
SLObjectItf player=NULL;
SLPlayItf playInterface;
SLAndroidSimpleBufferQueueItf pcmQue = NULL; // 播放隊列
const SLInterfaceID ids[]={SL_IID_BUFFERQUEUE};// 播放隊列接口參數
const SLboolean req[]={SL_BOOLEAN_TRUE};//表示接口是否開放
// 這裡必須注冊 SL_BOOLEAN_TRUE,SL_IID_BUFFERQUEUE 否則後面無法GetInterface到接口
//sizeof(ids)/ sizeof(SLInterfaceID) 播放隊列參數個數
re = (*eng)->CreateAudioPlayer(eng, &player, &ds, &audioSink, sizeof(ids)/ sizeof(SLInterfaceID) , ids,req );
if(re != SL_RESULT_SUCCESS){
LOGE("CreateAudioPlayer failed");
} else{
LOGW("CreateAudioPlayer success");
}
//執行個體化 播放器
(*player)->Realize(player,SL_BOOLEAN_FALSE);
// 獲得播放器接口
re = (*player)->GetInterface(player, SL_IID_PLAY,&playInterface);
if(re != SL_RESULT_SUCCESS){
LOGE("GetInterface failed");
}
re = (*player)->GetInterface(player,SL_IID_BUFFERQUEUE, &pcmQue);
if(re != SL_RESULT_SUCCESS){
LOGE("GetInterface SL_IID_BUFFERQUEUE failed");
}
建立播放器對象,從代碼看主要幹了:
- 建立CreateAudioPlayer播放器對象,并注冊了接口:SL_IID_BUFFERQUEUE ,具體參數可以看上面四闆斧中create的介紹。并且将上馬的混音器輸出裝置對象audioSink 和 輸出隊列音頻格式對象 ds傳給播放器。
- 執行個體化播放器,并且獲得播放器功能接口playInterface
4、設定播放回調函數,啟動引擎播放。
- 啟動引擎播放
// 設定回調函數,播放隊列為空調用
(*pcmQue)->RegisterCallback(pcmQue, PcmCall,0);
(*playInterface)->SetPlayState(playInterface, SL_PLAYSTATE_PLAYING);
// 啟動隊列回調,傳遞一個空字元串來啟動
(*pcmQue)->Enqueue(pcmQue,"",1);
啟動了引擎後,就不會不停的執行回調函數,是以隻需要在回調函數中壓入資料即可。
- 回調函數,壓入資料到播放隊列
void PcmCall(SLAndroidSimpleBufferQueueItf bf, void * context){
LOGW("PcmCall");
static FILE *fp = NULL;
static char *buf = NULL;
if(!buf)
{
buf = new char[1024*1024];
}
if(!fp)
{
fp = fopen("test.pcm","rb") ;
}
if(!fp) return;
if(feof(fp)==0)// =0 表示沒有到結尾
{
int len = fread(buf,1, 1024,fp);// 讀取到buf,一個機關多少,讀多大,fp
if(len > 0)
{
(*bf)-> Enqueue(bf, buf, len);// 發送到的bf, 發送的buf,長度
}
}
}
從代碼看出,是将檔案test.pcm 讀取出來,然後傳入到播放隊列,來進行播放的。播放引擎有資料就會播放,沒資料就會堵塞。
到這裡完整的播放pcm的功能就完成了。