溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android音頻開發(fā)(7):使用 OpenSL ES API(下)

發(fā)布時間:2020-07-18 21:20:02 來源:網(wǎng)絡(luò) 閱讀:23656 作者:Jhuster 欄目:移動開發(fā)

本文是我的《Android音頻開發(fā)》系列的第七篇文章,上一篇文章總整體上介紹了 Android OpenSL ES API 的基本概況,告訴了大家這個框架有什么特性,可以做什么,不能做什么。本文則重點(diǎn)介紹 OpenSL ES 框架及其API接口的一些關(guān)鍵的設(shè)計和概念,只有理解了它們,你才能更好地讀懂 OpenSL ES 的相關(guān)代碼。示例代碼則放到了文章的最后,相信大家理解了這些基本的概念后,就能很容易地讀懂這些代碼的細(xì)節(jié)了。


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


OpenSL ES 雖然是 C 語言編寫,但是它的接口采用的是面向?qū)ο蟮姆绞?,并不是提供一系列的函?shù)接口,而是以 Interface 的方式來提供 API,這是理解 OpenSL ES API 的一個比較重要的點(diǎn)。


可能這么說比較抽象,舉例來說,一般的 C 語言庫,比如:math 庫,提供的接口可能是這樣的:


double cosh(double);
double sinh(double);
double tanh(double);


我們直接在代碼中調(diào)用這個函數(shù)即可,但是 OpenSL ES 卻不是這樣提供 API 的,它的大都數(shù) API 需要這樣訪問:


// 下面代碼是對 Audio Engine 對象進(jìn)行 “初始化”
SLEngineItf engineObject;
SLresult result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);


由此可見,OpenSL ES 的 API 大都數(shù)是通過 “對象” 來調(diào)用的,如果在 Android NDK 下開發(fā)過 C 代碼,就應(yīng)該不會太陌生,因?yàn)槲覀冋{(diào)用 “JNI* env” 的函數(shù)也是這個樣子去調(diào)用的。


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 的時候需要做一些選擇和判斷


通過查看 “OpenSLES.h” 文件,我們可以看到 OpenSL ES 定義的所有 Object 對象的 ID,我們可以通過 Object ID 來創(chuàng)建對應(yīng)的對象實(shí)例。


Android音頻開發(fā)(7):使用 OpenSL ES API(下)


其中,我們比較常用的應(yīng)該就是:ENGINE、AUDIOPLAYER 和 AUDIORECORDER 對象了。


同樣,“OpenSLES.h” 文件中還定義了所有的 Interface ID,通過 Interface ID 我們可以從對象中獲取到對應(yīng)的功能接口。


Android音頻開發(fā)(7):使用 OpenSL ES API(下)


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


OpenSL ES 還有一個比較重要的概念,就是它的狀態(tài)機(jī)制,如圖所示:


Android音頻開發(fā)(7):使用 OpenSL ES API(下)

任何一個 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 來完成的,連 OpenSL ES 里面的 Object 也是通過 SLObjectItf Interface 來訪問和使用的。


4.1 Engine Object 和 SLEngineItf Interface


OpenSL ES 里面最核心的對象就是:Engine Object,音頻引擎對象,它主要提供如下兩個功能:


(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);


獲取管理接口:


SLEngineItf engineEngine;
(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &(engineEngine));


下面我們就可以愉快地使用 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 Data Source 和 Data Sink


OpenSL ES 里面,這兩個結(jié)構(gòu)體均是作為創(chuàng)建 Media Object 對象時的參數(shù)而存在的,data source 代表著輸入源的信息,即數(shù)據(jù)從哪兒來、輸入的數(shù)據(jù)參數(shù)是怎樣的;而 data sink 則代表著輸出的信息,即數(shù)據(jù)輸出到哪兒、以什么樣的參數(shù)來輸出。


4.3.1 基本定義


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)用場景來配置。


4.3.2 示例說明


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


例如,對于 player 而言:


Android音頻開發(fā)(7):使用 OpenSL ES API(下)


而對于 recorder 而言:


Android音頻開發(fā)(7):使用 OpenSL ES API(下)

5. 示例程序及參考資料


限于篇幅,關(guān)于 OpenSL ES 的 Player 和 Recorder 相關(guān) API 詳細(xì)的用法,就不在本文展開了,其實(shí)理解了上面介紹了這些概念之后,結(jié)合官方的 《OpenSL_ES_Specification_1.0.1.pdf》,以及我給出的示例代碼,很容易就能讀懂和掌握這些 API 的用法了,示例代碼地址如下:


https://github.com/Jhuster/AudioDemo


6. 小結(jié)


關(guān)于如何 Android OpenSL ES API 就介紹到這兒了,文章中有不清楚的地方歡迎留言或者來信 lujun.hust@gmail.com 交流,或者關(guān)注我的新浪微博 @盧_俊 或者 微信公眾號 @Jhuster 獲取最新的文章和資訊。


Android音頻開發(fā)(7):使用 OpenSL ES API(下)


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI