Open SL ES總結(jié)

1. 采用面向?qū)ο蟮?C 語言接口

??OpenSL ES 雖然是 C 語言編寫笛丙,但是它的接口采用的是面向?qū)ο蟮姆绞剑⒉皇翘峁┮幌盗械暮瘮?shù)接口价说,而是以 Interface 的方式來提供 API拯啦,這是理解 OpenSL ES API 的一個比較重要的點(diǎn)。
??例如熔任,實(shí)例化引擎的過程如下:

SLObjectItf slObjEngine = NULL;
SLresult result = (*slObjEngine)->Realize(slObjEngine, SL_BOOLEAN_FALSE);

??由于C語言不存在this指針褒链,所以參數(shù)中需要將對象本身傳入。

2. Objects 和 Interfaces

??OpenSL ES 有兩個必須理解的概念疑苔,就是Object 和 Interface甫匹,Object 可以想象成 Java 的 Object 類,Interface 可以想象成 Java 的 Interface惦费,但它們并不完全相同兵迅,下面進(jìn)一步解釋他們的關(guān)系:
??(1) 每個 Object 可能會存在一個或者多個 Interface,官方為每一種 Object 都定義了一系列的 Interface
??(2)每個 Object 對象都提供了一些最基礎(chǔ)的操作薪贫,比如:Realize恍箭,Resume,GetState瞧省,Destroy 等等扯夭,如果希望使用該對象支持的功能函數(shù)鳍贾,則必須通過其 GetInterface 函數(shù)拿到 Interface 接口,然后通過 Interface 來訪問功能函數(shù)
??(3)并不是每個系統(tǒng)上都實(shí)現(xiàn)了 OpenSL ES 為 Object 定義的所有 Interface交洗,所以在獲取 Interface 的時候需要做一些選擇和判斷
??如SLObjectItf_的定義如下:

struct SLObjectItf_ {
    SLresult (*Realize) (
        SLObjectItf self,
        SLboolean async
    );
    SLresult (*Resume) (
        SLObjectItf self,
        SLboolean async
    );
    SLresult (*GetState) (
        SLObjectItf self,
        SLuint32 * pState
    );
    SLresult (*GetInterface) (
        SLObjectItf self,
        const SLInterfaceID iid,
        void * pInterface
    );
    SLresult (*RegisterCallback) (
        SLObjectItf self,
        slObjectCallback callback,
        void * pContext
    );
    void (*Destroy) (
        SLObjectItf self
    );

    ...
};

3. OpenSL ES 的狀態(tài)機(jī)制

??任何一個 OpenSL ES 的對象骑科,創(chuàng)建成功后,都進(jìn)入 SL_OBJECT_STATE_UNREALIZED 狀態(tài)构拳,這種狀態(tài)下咆爽,系統(tǒng)不會為它分配任何資源,直到調(diào)用 Realize 函數(shù)為止置森。

??Realize 后的對象斗埂,就會進(jìn)入 SL_OBJECT_STATE_REALIZED 狀態(tài),這是一種“可用”的狀態(tài)凫海,只有在這種狀態(tài)下呛凶,對象的各個功能和資源才能正常地訪問。

??當(dāng)一些系統(tǒng)事件發(fā)生后盐碱,比如出現(xiàn)錯誤或者 Audio 設(shè)備被其他應(yīng)用搶占,OpenSL ES 對象會進(jìn)入 SL_OBJECT_STATE_SUSPENDED 狀態(tài)沪伙,如果希望恢復(fù)正常使用瓮顽,需要調(diào)用 Resume 函數(shù)。

??當(dāng)調(diào)用對象的 Destroy 函數(shù)后围橡,則會釋放資源暖混,并回到 SL_OBJECT_STATE_UNREALIZED 狀態(tài)。

??簡言之翁授,一個 OpenSL ES 對象的生命周期拣播,就是從 create 到 destroy 的過程,生命周期的控制收擦,都是通過開發(fā)者顯示調(diào)用來完成的贮配。

4. 常用的對象和結(jié)構(gòu)體

??在 OpenSL ES 中,一切 API 的訪問和控制都是通過 Interface 來完成的塞赂。

4.1 Engine Object 和 SLEngineItf Interface

??(1)管理 Audio Engine 的生命周期
??(2)提供管理接口: SLEngineItf泪勒,該接口可以用來創(chuàng)建所有其他的 Object 對象
??(3)提供設(shè)備屬性查詢接口:SLEngineCapabilitiesItf 和 SLAudioIODeviceCapabilitiesItf,這些接口可以查詢設(shè)備的一些屬性信息

??Engine Object 對象的創(chuàng)建方法如下:

SLObjectItf engineObject;
slCreateEngine( &engineObject, 0, nullptr, 0, nullptr, nullptr );

??初始化/銷毀:

(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
(*engineObject)->Destroy(engineObject);

??下面我們就可以愉快地使用 engineEngine 來創(chuàng)建所有 OpenSL ES 的其他對象了宴猾。

4.2 Media Object

??OpenSL ES 里面另一組比較重要的對象就是 Media Object 圆存,代表著多媒體功能的抽象,比如:player仇哆、recorder 等等沦辙。

??我們可以通過 SLEngineItf 提供的 CreateAudioPlayer 方法來創(chuàng)建一個 player 對象實(shí)例,可以通過 SLEngineItf 提供的 CreateAudioRecorder 方法來創(chuàng)建一個 recorder 實(shí)例讹剔。

4.3 SLDataSource 和 SLDataSink

??OpenSL ES 里面油讯,這兩個結(jié)構(gòu)體均是作為創(chuàng)建 Media Object 對象時的參數(shù)而存在的详民,
??SLDataSource 代表著輸入源的信息,即數(shù)據(jù)從哪兒來撞羽、輸入的數(shù)據(jù)參數(shù)是怎樣的阐斜;
??SLDataSink 則代表著輸出的信息,即數(shù)據(jù)輸出到哪兒诀紊、以什么樣的參數(shù)來輸出谒出。
??Data Source 的定義如下:

typedef struct SLDataSource_ {
      void *pLocator;
      void *pFormat;
} SLDataSource;

??Data Sink 的定義如下:

typedef struct SLDataSink_ {
    void *pLocator;
    void *pFormat;
} SLDataSink;

??其中,pLocator 主要有如下幾種:

SLDataLocator_Address
SLDataLocator_BufferQueue
SLDataLocator_IODevice
SLDataLocator_MIDIBufferQueue
SLDataLocator_URI

??也就是說邻奠,Media Object 對象的輸入源/輸出源笤喳,既可以是 URL,也可以 Device碌宴,或者來自于緩沖區(qū)隊(duì)列等等杀狡,完全是由 Media Object 對象的具體類型和應(yīng)用場景來配置。

??不同的 Media Object 對象實(shí)例贰镣,data source 和 data sink 的具體內(nèi)容是不一樣的呜象。

??對于 player 而言:


image.png

對于 recorder 而言:

image.png

引自:https://blog.51cto.com/ticktick/1771239
下面是一個播放音頻的完整示例:

#include <jni.h>
#include <string>
#include <android/log.h>

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

#define  LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"test",__VA_ARGS__)

static SLObjectItf  g_slObjEngine = NULL;

SLEngineItf CreatSLEngineItf()
{
    //a 創(chuàng)建引擎對象
    SLresult result = slCreateEngine(&g_slObjEngine, 0, 0, 0, 0, 0);
    if (result != SL_RESULT_SUCCESS)
        return  NULL;

    //b 實(shí)例化引擎對象
    result = (*g_slObjEngine)->Realize(g_slObjEngine, SL_BOOLEAN_FALSE); //SL_BOOLEAN_FALSE等待對象創(chuàng)建
    if (result != SL_RESULT_SUCCESS)
        return  NULL;

    //c 獲取接口
    SLEngineItf slEngineItf;
    result = (*g_slObjEngine)->GetInterface(g_slObjEngine, SL_IID_ENGINE, &slEngineItf);
    if (result != SL_RESULT_SUCCESS)
        return  NULL;

    return slEngineItf;
}

//回調(diào)函數(shù)
void AudioCallBack(SLAndroidSimpleBufferQueueItf bf, void *context)
{
    LOGE("AudioCallBack ");
    static FILE *s_file   = NULL;
    static char *s_buf    = NULL;

    if (!s_buf) {
        s_buf = new char[1024 * 1024];
    }
    if (!s_file) {
        s_file = fopen("/sdcard/test.pcm", "rb");
    }
    if (!s_file) {
        LOGE("fopen failed!");
        return;
    }

    if (feof(s_file) == 0) {
        int len = fread(s_buf, 1, 1024, s_file);
        if (len > 0) {
            (*bf)->Enqueue(bf, s_buf, len);
        }
    }
}


void playAudio() {
    //1 創(chuàng)建引擎
    SLEngineItf pEngineItf = CreatSLEngineItf();
    if (pEngineItf)
    {
        LOGE("CreatSLEngineItf success!");

    } else
    {
        LOGE("CreatSLEngineItf failed!");
    }

    //2 創(chuàng)建混音器
    //----輸出混音器
    SLObjectItf slObjMix = NULL;
    SLresult result = (*pEngineItf)->CreateOutputMix(pEngineItf, &slObjMix, 0, 0, 0);
    if (result != SL_RESULT_SUCCESS)
    {
        LOGE("CreateOutputMix failed!");
    };

    //----實(shí)例化
    result = (*slObjMix)->Realize(slObjMix, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS)
    {
        LOGE("slObjMix Realize failed!");
    };

    SLDataLocator_OutputMix outmix = {SL_DATALOCATOR_OUTPUTMIX, slObjMix};
    SLDataSink slDataSink = {&outmix, 0};

    //3 配置音頻信息
    //----創(chuàng)建緩沖隊(duì)列
    SLDataLocator_AndroidSimpleBufferQueue bufQueue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 10};

    //----音頻格式配置
    SLDataFormat_PCM dataFormat_pcm = {
            SL_DATAFORMAT_PCM,
            2,                                                   //通道數(shù)
            SL_SAMPLINGRATE_44_1,                             //采樣率
            SL_PCMSAMPLEFORMAT_FIXED_16,                     // bitsPerSample
            SL_PCMSAMPLEFORMAT_FIXED_16,                     // containerSize
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,  //聲道
            SL_BYTEORDER_LITTLEENDIAN                        //字節(jié)序,小端
    };
    //----播放器使用的結(jié)構(gòu)體
    SLDataSource slDataSource = {&bufQueue, &dataFormat_pcm};

    // 4 創(chuàng)建播放器
    SLObjectItf  slObjPlayer = NULL;
    const SLInterfaceID  itfIds[] = { SL_IID_BUFFERQUEUE };          //接口id
    const SLboolean      itfReqs[] = { SL_BOOLEAN_TRUE  };          //接口開放

    result = (*pEngineItf)->CreateAudioPlayer(pEngineItf, &slObjPlayer, &slDataSource, &slDataSink,
                                              sizeof(itfIds) / sizeof(SLInterfaceID), itfIds, itfReqs);
    if (result != SL_RESULT_SUCCESS)
    {
        LOGE("CreateAudioPlayer  failed!");
    } else {
        LOGE("CreateAudioPlayer  success!");
    };
    //實(shí)例化
    (*slObjPlayer)->Realize(slObjPlayer, SL_BOOLEAN_FALSE);

    //獲取接口
    SLPlayItf slPlayItf = NULL;
    result = (*slObjPlayer)->GetInterface(slObjPlayer, SL_IID_PLAY, &slPlayItf);
    if (result != SL_RESULT_SUCCESS)
    {
        LOGE("slObjPlayer GetInterface  failed!");
    } else{
        LOGE("slObjPlayer GetInterface  success!");
    };

    //獲取緩沖隊(duì)列接口
    SLAndroidSimpleBufferQueueItf slBufQueue = NULL;
    result =(*slObjPlayer)->GetInterface(slObjPlayer, SL_IID_BUFFERQUEUE, &slBufQueue);
    if (result != SL_RESULT_SUCCESS)
    {
        LOGE("slObjPlayer GetInterface BUFFERQUEUE failed!");
    } else {
        LOGE("slObjPlayer GetInterface BUFFERQUEUE success!");
    };

    //設(shè)置回調(diào)函數(shù),播放隊(duì)列為空的時候調(diào)用
    //第二個參數(shù)是回調(diào)函數(shù) 第三個參數(shù)是 給回調(diào)函數(shù)傳的參數(shù)
    (*slBufQueue)->RegisterCallback(slBufQueue, AudioCallBack, 0);
    //設(shè)置狀態(tài) 播放
    (*slPlayItf)->SetPlayState(slPlayItf, SL_PLAYSTATE_PLAYING);

    //啟動隊(duì)列回調(diào)
    (*slBufQueue)->Enqueue(slBufQueue, "", 1);
}

參考:https://blog.csdn.net/u010141160/article/details/82871244
https://www.cnblogs.com/renhui/p/9565464.html
http://www.reibang.com/p/2b8d2de9a47b

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碑隆,隨后出現(xiàn)的幾起案子恭陡,更是在濱河造成了極大的恐慌,老刑警劉巖上煤,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件休玩,死亡現(xiàn)場離奇詭異,居然都是意外死亡劫狠,警方通過查閱死者的電腦和手機(jī)拴疤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來独泞,“玉大人呐矾,你說我怎么就攤上這事∨成埃” “怎么了凫佛?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長孕惜。 經(jīng)常有香客問我愧薛,道長,這世上最難降的妖魔是什么衫画? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任毫炉,我火速辦了婚禮,結(jié)果婚禮上削罩,老公的妹妹穿的比我還像新娘瞄勾。我一直安慰自己费奸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布进陡。 她就那樣靜靜地躺著愿阐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趾疚。 梳的紋絲不亂的頭發(fā)上缨历,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音糙麦,去河邊找鬼辛孵。 笑死,一個胖子當(dāng)著我的面吹牛赡磅,可吹牛的內(nèi)容都是我干的魄缚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼焚廊,長吁一口氣:“原來是場噩夢啊……” “哼冶匹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咆瘟,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嚼隘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搞疗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗓蘑,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡须肆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年匿乃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豌汇。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡幢炸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拒贱,到底是詐尸還是另有隱情宛徊,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布逻澳,位于F島的核電站闸天,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏斜做。R本人自食惡果不足惜苞氮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓤逼。 院中可真熱鬧笼吟,春花似錦库物、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撵枢,卻和暖如春民晒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诲侮。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工镀虐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沟绪。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓刮便,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绽慈。 傳聞我的和親對象是個殘疾皇子恨旱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容