編輯:關於Android編程
本文是我的《Android音頻開發》系列的第七篇文章,上一篇文章總整體上介紹了 Android OpenSL ES API 的基本概況,告訴了大家這個框架有什麼特性,可以做什麼,不能做什麼。本文則重點介紹 OpenSL ES 框架及其API接口的一些關鍵的設計和概念,只有理解了它們,你才能更好地讀懂 OpenSL ES 的相關代碼。示例代碼則放到了文章的最後,相信大家理解了這些基本的概念後,就能很容易地讀懂這些代碼的細節了。
1. 面向對象的 C 語言接口
OpenSL ES 雖然是 C 語言編寫,但是它的接口采用的是面向對象的方式,並不是提供一系列的函數接口,而是以 Interface 的方式來提供 API,這是理解 OpenSL ES API 的一個比較重要的點。
可能這麼說比較抽象,舉例來說,一般的 C 語言庫,比如:math 庫,提供的接口可能是這樣的:
doublecosh(double); doublesinh(double); doubletanh(double);
我們直接在代碼中調用這個函數即可,但是 OpenSL ES 卻不是這樣提供 API 的,它的大都數 API 需要這樣訪問:
//下面代碼是對AudioEngine對象進行“初始化” SLEngineItfengineObject; SLresultresult=(*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE);
由此可見,OpenSL ES 的 API 大都數是通過 “對象” 來調用的,如果在 Android NDK 下開發過 C 代碼,就應該不會太陌生,因為我們調用 “JNI* env” 的函數也是這個樣子去調用的。
2. Objects 和 Interfaces
OpenSL ES 有兩個必須理解的概念,就是 Object 和 Interface,Object 可以想象成 Java 的 Object 類,Interface 可以想象成 Java 的 Interface,但它們並不完全相同,下面進一步解釋他們的關系:
(1) 每個 Object 可能會存在一個或者多個 Interface,官方為每一種 Object 都定義了一系列的 Interface
(2)每個 Object 對象都提供了一些最基礎的操作,比如:Realize,Resume,GetState,Destroy 等等,如果希望使用該對象支持的功能函數,則必須通過其 GetInterface 函數拿到 Interface 接口,然後通過 Interface 來訪問功能函數
(3)並不是每個系統上都實現了 OpenSL ES 為 Object 定義的所有 Interface,所以在獲取 Interface 的時候需要做一些選擇和判斷
通過查看 “OpenSLES.h” 文件,我們可以看到 OpenSL ES 定義的所有 Object 對象的 ID,我們可以通過 Object ID 來創建對應的對象實例。
其中,我們比較常用的應該就是:ENGINE、AUDIOPLAYER 和 AUDIORECORDER 對象了。
同樣,“OpenSLES.h” 文件中還定義了所有的 Interface ID,通過 Interface ID 我們可以從對象中獲取到對應的功能接口。
3. OpenSL ES 的狀態機制
OpenSL ES 還有一個比較重要的概念,就是它的狀態機制,如圖所示:
任何一個 OpenSL ES 的對象,創建成功後,都進入 SL_OBJECT_STATE_UNREALIZED 狀態,這種狀態下,系統不會為它分配任何資源,直到調用 Realize 函數為止。
Realize 後的對象,就會進入 SL_OBJECT_STATE_REALIZED 狀態,這是一種“可用”的狀態,只有在這種狀態下,對象的各個功能和資源才能正常地訪問。
當一些系統事件發生後,比如出現錯誤或者 Audio 設備被其他應用搶占,OpenSL ES 對象會進入 SL_OBJECT_STATE_SUSPENDED 狀態,如果希望恢復正常使用,需要調用 Resume 函數。
當調用對象的 Destroy 函數後,則會釋放資源,並回到 SL_OBJECT_STATE_UNREALIZED 狀態。
簡言之,一個 OpenSL ES 對象的生命周期,就是從 create 到 destroy 的過程,生命周期的控制,都是通過開發者顯示調用來完成的。
4. 常用的對象和結構體
心中保持一個概念,就是在 OpenSL ES 中,一切 API 的訪問和控制都是通過 Interface 來完成的,連 OpenSL ES 裡面的 Object 也是通過 SLObjectItf Interface 來訪問和使用的。
4.1 Engine Object 和 SLEngineItf Interface
OpenSL ES 裡面最核心的對象就是:Engine Object,音頻引擎對象,它主要提供如下兩個功能:
(1)管理 Audio Engine 的生命周期
(2)提供管理接口: SLEngineItf,該接口可以用來創建所有其他的 Object 對象
(3)提供設備屬性查詢接口:SLEngineCapabilitiesItf 和 SLAudioIODeviceCapabilitiesItf,這些接口可以查詢設備的一些屬性信息
Engine Object 對象的創建方法如下:
SLObjectItfengineObject; slCreateEngine(&engineObject,0,nullptr,0,nullptr,nullptr);
初始化/銷毀:
(*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE); (*engineObject)->Destroy(engineObject);
獲取管理接口:
SLEngineItfengineEngine; (*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&(engineEngine));
下面我們就可以愉快地使用 engineEngine 來創建所有 OpenSL ES 的其他對象了。
4.2 Media Object
OpenSL ES 裡面另一組比較重要的對象就是 Media Object ,代表著多媒體功能的抽象,比如:player、recorder 等等。
我們可以通過 SLEngineItf 提供的 CreateAudioPlayer 方法來創建一個 player 對象實例,可以通過 SLEngineItf 提供的 CreateAudioRecorder 方法來創建一個 recorder 實例。
4.3 Data Source 和 Data Sink
OpenSL ES 裡面,這兩個結構體均是作為創建 Media Object 對象時的參數而存在的,data source 代表著輸入源的信息,即數據從哪兒來、輸入的數據參數是怎樣的;而 data sink 則代表著輸出的信息,即數據輸出到哪兒、以什麼樣的參數來輸出。
4.3.1 基本定義
Data Source 的定義如下:
typedefstructSLDataSource_{ void*pLocator; void*pFormat; }SLDataSource;
Data Sink 的定義如下:
typedefstructSLDataSink_{ void*pLocator; void*pFormat; }SLDataSink;
其中,pLocator 主要有如下幾種:
SLDataLocator_Address SLDataLocator_BufferQueue SLDataLocator_IODevice SLDataLocator_MIDIBufferQueue SLDataLocator_URI
也就是說,Media Object 對象的輸入源/輸出源,既可以是 URL,也可以 Device,或者來自於緩沖區隊列等等,完全是由 Media Object 對象的具體類型和應用場景來配置。
4.3.2 示例說明
不同的 Media Object 對象實例,data source 和 data sink 的具體內容是不一樣的。
例如,對於 player 而言:
而對於 recorder 而言:
5. 示例程序及參考資料
限於篇幅,關於 OpenSL ES 的 Player 和 Recorder 相關 API 詳細的用法,就不在本文展開了,其實理解了上面介紹了這些概念之後,結合官方的《OpenSL_ES_Specification_1.0.1.pdf》,以及我給出的示例代碼,很容易就能讀懂和掌握這些 API 的用法了,示例代碼地址如下:
https://github.com/Jhuster/AudioDemo
如今的人們幾乎每天都會用手機拍攝無數張照片,但是照片一多管理就是一件麻煩事。一般來說,用戶管理照片不是系統的圖庫就是快圖應用,後者雖然浏覽速度快但管理功能卻
其實寫分析源碼文章總會顯得很復雜很乏味,但是梳理自己看源碼時的一些總結也是一種提高。這篇博客分析下Activity啟動過程源碼,我會盡量說得簡單點。個人的觀點是看源碼不能
MSM8909+Android5.1.1SPI驅動開發(PSAM部分) 1. PSAM部分的硬件設計 圖1CS 片選信號SCK 時鐘信號MISO 主設
類加載流程在周志明寫的<<深入理解java虛擬機的一本書中>>已經詳細地介紹java加載類過程,在HotSpot虛擬機實現中是通過雙親委派機制來加