OpenSL ES: OpenSL ES 簡介


1. OpenSL ES 是什么

OpenSL ES (Open Sound Library for Embedded Systems)是無授權費、跨平台、針對嵌入式系統精心優化的硬件音頻加速API。它為嵌入式移動多媒體設備上的本地應用程序開發者提供標准化, 高性能,低響應時間的音頻功能實現方法,並實現軟/硬件音頻性能的直接跨平台部署,降低執行難度,促進高級音頻市場的發展。簡單來說OpenSL ES是一個嵌入式跨平台免費的音頻處理庫。
Android的OpenSL ES庫是在NDK的platforms文件夾對應android平台先相應cpu類型里面,如:

2. OpenSL ES 與 Android的關系

Android 2.3 (API 9) 即開始支持 OpenSL ES 標准了,通過 NDK 提供相應的 API 開發接口,下圖是 Android 官方給出的關系圖(參考:https://source.android.com/devices/audio/latency_app.html):

由該圖可以看出,Android 實現的 OpenSL ES 只是 OpenSL 1.0.1 的子集,並且進行了擴展,因此,對於 OpenSL ES API 的使用,我們還需要特別留意哪些是 Android 支持的,哪些是不支持的,具體相關文檔的地址位於 NDK docs 目錄下:

NDKroot/docs/Additional_library_docs/opensles/index.html
NDKroot/docs/Additional_library_docs/opensles/OpenSL_ES_Specification_1.0.1.pdf

3. 與Java層的AudioRecord, AudioTrack的關系

利用 Android 提供的 AudioRecord 采集音頻,利用 AudioTrack 播放音頻,利用 MediaCodec 來編解碼,這些 API 均是 Android 提供的 Java 層 API,無論是采集、播放還是編解碼,這些 API 接口都需要將音頻數據從 Java 拷貝到 native 層,或者從 native 層拷貝到 Java,如果希望減少拷貝,開發更加高效的 Android 音頻應用,則建議使用 Android NDK 提供的 OpenSL ES API 接口,它支持在 native 層直接處理音頻數據。

4. OpenSL ES的特性以及優劣勢

特性:

1)C 語言接口,兼容 C++,需要在 NDK 下開發,能更好地集成在 native 應用中
(2)運行於 native 層,需要自己管理資源的申請與釋放,沒有 Dalvik 虛擬機的垃圾回收機制
(3)支持 PCM 數據的采集,支持的配置:16bit 位寬,16000 Hz采樣率,單通道。(其他的配置不能保證兼容所有平台)
(4)支持 PCM 數據的播放,支持的配置:8bit/16bit 位寬,單通道/雙通道,小端模式,采樣率(8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 Hz)
(5)支持播放的音頻數據來源:res 文件夾下的音頻、assets 文件夾下的音頻、sdcard 目錄下的音頻、在線網絡音頻、代碼中定義的音頻二進制數據等等

優勢:

1)避免音頻數據頻繁在 native 層和 Java 層拷貝,提高效率
(2)相比於 Java API,可以更靈活地控制參數
(3)由於是 C 代碼,因此可以做深度優化,比如采用 NEON 優化
(4)代碼細節更難被反編譯

劣勢:

1)不支持版本低於 Android 2.3 (API 9) 的設備
(2)沒有全部實現 OpenSL ES 定義的特性和功能
(3)不支持 MIDI 
(4)不支持直接播放 DRM 或者 加密的內容
(5)不支持音頻數據的編解碼,如需編解碼,需要使用 MediaCodec API 或者第三方庫
(6)在音頻延時方面,相比於上層 API,並沒有特別明顯地改進

5. OpenSL ES的一些基本概念

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

所有的Object在OpenSL里面我們拿到的都是一個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 (*AbortAsyncOperation) (SLObjectItf self);

    void (*Destroy) (SLObjectItf self);

    SLresult (*SetPriority) (SLObjectItf self, SLint32 priority, SLboolean preemptable);

    SLresult (*GetPriority) (SLObjectItf self, SLint32 *pPriority, SLboolean *pPreemptable);

    SLresult (*SetLossOfControlInterfaces) (SLObjectItf self, SLint16 numInterfaces, SLInterfaceID * pInterfaceIDs, SLboolean enabled);
};

typedef const struct SLObjectItf_ * const * SLObjectItf;

這里的SLObjectItf是一個二級指針,它指向的是一個結構體指針。任何創建出來的Object都必須調用 Realize 方法做初始化,在不需要的時候可以使用 Destroy 方法來釋放資源。

Interface則是方法的集合,例如SLRecordItf里面包含了和錄音相關的方法,SLPlayItf包含了和播放相關的方法。我們功能都是通過調用Interfaces的方法去實現的,如 SLEngineItf 這個interface,我們可以通過它去創建各種Object,例如播放器、錄音器、混音器的Object,然后在用這些Object去獲取各種Interface去實現各種功能。

struct SLEngineItf_ {
    SLresult (*CreateAudioPlayer) (SLEngineItf self, SLObjectItf * pPlayer, SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces, const SLInterfaceID * pInterfaceIds, const SLboolean * pInterfaceRequired);

    SLresult (*CreateAudioRecorder) (SLEngineItf self, SLObjectItf * pRecorder, SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces, const SLInterfaceID * pInterfaceIds, const SLboolean * pInterfaceRequired);

    SLresult (*CreateOutputMix) (SLEngineItf self, SLObjectItf * pMix, SLuint32 numInterfaces, const SLInterfaceID * pInterfaceIds, const SLboolean * pInterfaceRequired);

    ...
};

6. OpenSL ES的基本開發流程

這里以使用OpenSL ES 來播放一個PCM文件為例,主要包括:

1、 創建接口對象
2、設置混音器
3、創建播放器(錄音器)
4、設置緩沖隊列和回調函數
5、設置播放狀態
6、啟動回調函數

由於OpenSL ES是Native層提供的API,在使用前須注意添加頭文件和鏈接選項,在需要用到OpenSL ES API的C文件中添加:

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

鏈接庫文件:

如果是:Android.mk

LOCAL_LDLIBS += -lOepnSLES

如果是:CMakeLists.txt

 在 target_link_libraries 括號中添加 OpenSLES 

示例代碼: 《OpenSL ES: 利用OpenSL ES播放一個存在於SDcard上的PCM文件

參考鏈接:

1. Android音頻開發(7):使用 OpenSL ES API(下)

2. OpenSL ES 學習筆記

3.Android OpenSL ES 開發:Android OpenSL 介紹和開發流程說明

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM