適用於 Android 的 OpenSL ES

警告:OpenSL ES 已淘汰。開發人員應使用 GitHub 提供的開放原始碼 Oboe 程式庫。Oboe 是 C++ 包裝函式,提供與 AAudio 非常相似的 API。Oboe 會在 AAudio 可用時呼叫 AAudio,而在 AAudio 不可用時則會改回使用 OpenSL ES。

本頁詳細介紹了 OpenSL ES™ 的 NDK 實作與 OpenSL ES 1.0.1 的參考規範有何不同。使用規格說明中的程式碼範例時,您可能需要加以修改,才能在 Android 上執行。

除非另有說明,否則所有功能均適用於 Android 2.3 (API 級別 9) 以上版本。部分功能僅適用於 Android 4.0 (API 級別 14),請參閱相關說明。

注意:Android 相容性定義說明文件 (CDD) 中列有相容 Android 裝置的硬體和軟體需求。請參閱 Android 相容性詳細瞭解整體相容性計畫,並參閱 CDD 以取得實際 CDD 說明文件。

OpenSL ES 提供 C 語言介面,且可使用 C++ 存取此介面。此外,OpenSL ES 提供類似以下 Android Java API 音訊部分的功能:

和所有 Android 原生開發套件 (NDK) 一樣,適用於 Android 的 OpenSL ES 的主要用途是,協助實作可透過 Java 原生介面 (JNI) 進行呼叫的共用程式庫。NDK 不適用於編寫純 C/C++ 應用程式。不過,OpenSL ES 是功能全面的 API,我們預期您應只需使用此 API,就能滿足大部分音訊需求,無需向上呼叫在 Android 執行階段中執行的程式碼。

注意:Android 原生音訊 (高效能音訊) API 雖是以 OpenSL ES 為基礎,但並不是任何 OpenSL ES 1.0.1 設定檔 (遊戲、音樂,或電話) 的合格實作。這是因為 Android 並未實作任何一個設定檔所需的所有功能。Android 擴充功能頁面說明了 Android 行為與規範不同的所有已知情況。

從參考規範沿用的功能

OpenSL ES 的 Android NDK 實作項目從參考規格的功能集繼承了大部分功能,但有某些限制。

全域進入點

Android 版 OpenSL ES 支援 Android 規格中的所有全域進入點,包括:

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

物件和介面

下表列出了 OpenSL ES 的 Android NDK 實作支援的物件和介面。如果儲存格中為「是」,表示此實作項目支援該功能。

Android NDK 支援的物件和介面。

功能 音訊播放器 錄音工具 引擎 混音輸出
低音強化
緩衝區佇列 不可以
緩衝區佇列資料定位器 是:來源 不可以
動態介面管理
效果傳送 不可以
引擎
環境回響 不可以
等化器
I/O 裝置資料定位器 是:來源
中繼資料擷取 是:解碼為 PCM 不可以
靜音獨奏 不可以
物件
混音輸出定位器 是:接收器 不可以
播放 不可以
播放速率 不可以
預先擷取狀態 不可以
預設回響 不可以
錄製
跳轉 不可以
URI 資料定位器 是:來源 不可以
虛擬器
音量 不可以

下一節將說明部分上述功能的限制。

限制

表 1 中的功能存在特定限制,這些限制就是與參考規範不同的地方。本部分後續內容將介紹這些差異。

動態介面管理

Android 版 OpenSL ES 不支援 RemoveInterfaceResumeInterface

效果組合:環境回響和預設回響

在同一個混音輸出中,無法同時使用環境回響和預設回響。

如果平台預估 CPU 負載會過高,就可能忽略效果要求。

效果傳送

針對每個音訊播放器,SetSendLevel() 支援一個傳送電平。

環境回響

環境回響不支援 SLEnvironmentalReverbSettings 結構體的 reflectionsDelayreflectionsLevelreverbDelay 欄位。

MIME 資料格式

MIME 資料格式只能與 URI 資料定位器結合使用,而且只能用於音訊播放器,此資料格式無法用於錄音工具。

OpenSL ES 的 Android 實作要求將 mimeType 初始化為 NULL,或有效的 UTF-8 字串。您也必須將 containerType 初始化為有效值。如果沒有其他考量事項,例如移植到其他實作項目的可能性,或應用程式無法透過標頭辨識的內容格式,建議您將 mimeType 設為 NULL,並將 containerType 設為 SL_CONTAINERTYPE_UNSPECIFIED

適用於 Android 的 OpenSL ES 支援以下音訊格式,但前提是 Android 平台也支援這些格式:

  • WAV PCM。
  • WAV alaw。
  • WAV ulaw。
  • MP3 Ogg Vorbis。
  • AAC LC。
  • HE-AACv1 (AAC+)。
  • HE-AACv2 (增強型 AAC+)。
  • AMR。
  • FLAC。

注意:如需 Android 支援的音訊格式清單,請參閱支援的媒體格式

在 OpenSL ES 的 Android 實作項目中處理上述及其他音訊格式時,存在以下限制:

  • AAC 格式必須位於 MP4 或 ADTS 容器中。
  • 適用於 Android 的 OpenSL ES 不支援 MIDI
  • WMA 不屬於 AOSP,我們尚未驗證 WMA 是否與適用於 Android 的 OpenSL ES 相容。
  • OpenSL ES 的 Android NDK 實作不支援直接播放 DRM 或加密內容。如要播放受保護的音訊內容,您必須先在應用程式中進行解密,然後才能使用可強制執行任何 DRM 限制的應用程式播放此音訊內容。

Android 版 OpenSL ES 不支援以下物件操控方法:

  • Resume()
  • RegisterCallback()
  • AbortAsyncOperation()
  • SetPriority()
  • GetPriority()
  • SetLossOfControlInterfaces()

PCM 資料格式

PCM 是唯一一種可用於緩衝區佇列的資料格式。支援的 PCM 播放設定具有下列特性:

  • 8 位元無符號或 16 位元有符號。
  • 單聲道或立體聲。
  • 小端序位元組排序。
  • 取樣率如下:
    • 8,000 Hz。
    • 11,025 Hz。
    • 12,000 Hz。
    • 16,000 Hz。
    • 22,050 Hz。
    • 24,000 Hz。
    • 32,000 Hz。
    • 44,100 Hz。
    • 48,000 Hz。

Android 版 OpenSL ES 支援的錄製設定須視裝置而定。一般而言,無論何種裝置,都支援 16,000 Hz 單聲道/16 位元有正負號設定。

儘管名稱容易引起誤會,但 samplesPerSec 欄位的值是以 milliHz 為單位。為避免不小心使用錯誤的值,建議您使用其中一個專用的符號常數 (例如 SL_SAMPLINGRATE_44_1),初始化這個欄位。

Android 5.0 (API 級別 21) 以上版本支援浮點資料

播放速率

OpenSL ES 播放速率 代表物件呈現資料的速度,以正常速度的千分比 (或千分率) 表示。例如,一千分之 1,000 的播放速率為 1,000/1,000,即正常速度。「速率範圍」是一個封閉區間,表示一系列可能的播放速率。

因平台版本和實作不同,對播放速率範圍和其他功能的支援情況也存在差異。應用程式可在執行階段使用 PlaybackRate::GetRateRange()PlaybackRate::GetCapabilitiesOfRate() 查詢裝置,確定這些功能。

對於 PCM 格式的資料來源,裝置一般支援相同的速率範圍。若是其他格式,則支援千分之 1000 到千分之 1000 的統一速率範圍,也就是說,統一速率範圍實際上是單一值。

錄製

Android 版 OpenSL ES 不支援 SL_RECORDEVENT_HEADATLIMITSL_RECORDEVENT_HEADMOVING 事件。

跳轉

SetLoop() 方法可進行全檔案循環。如要啟用循環播放,請將 startPos 參數設為 0,並將 endPos 參數設為 SL_TIME_UNKNOWN

緩衝區佇列資料定位器

若是具有緩衝區佇列資料定位器的音訊播放器或錄音工具,只支援 PCM 資料格式。

I/O 裝置資料定位器

如果您已將 I/O 裝置資料定位器指定為 Engine::CreateAudioRecorder() 的資料來源,則適用於 Android 的 OpenSL ES 將僅支援使用此定位器。請使用以下程式碼片段中包含的值,初始化裝置資料定位器:

SLDataLocator_IODevice loc_dev =
  {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
  SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};

URI 資料定位器

適用於 Android 的 OpenSL ES 只能將 URI 資料定位器與 MIME 資料格式結合使用,並且僅可用於音訊播放器,URI 資料定位器無法用於錄音工具。URI 只能使用 http:file: 配置,不得使用 https:ftp:content: 等其他配置。

我們尚未驗證 Android 平台是否支援含音訊的 rtsp:

資料結構

Android 支援以下 OpenSL ES 1.0.1 資料結構:

  • SLDataFormat_MIME
  • SLDataFormat_PCM
  • SLDataLocator_BufferQueue
  • SLDataLocator_IODevice
  • SLDataLocator_OutputMix
  • SLDataLocator_URI
  • SLDataSink
  • SLDataSource
  • SLEngineOption
  • SLEnvironmentalReverbSettings
  • SLInterfaceID

平台設定

適用於 Android 的 OpenSL ES 專為多執行緒應用程式而設計,並且符合執行緒安全要求。它支援每個應用程式一個引擎,每個引擎最多 32 個物件。可用的裝置記憶體和 CPU 可能會進一步限制可用物件數量。

系統會辨識以下引擎選項,但 slCreateEngine 會忽略這些選項:

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

在同一個應用程式中可以一起使用 OpenMAX AL 和 OpenSL ES。在這種情況下,內部存在一個共用引擎物件,而 OpenMAX AL 和 OpenSL ES 會共用 32 個物件的限制。應用程式應該建立兩個引擎,同時使用這兩個引擎,最後將其銷毀。實作項目會維護共用引擎的參考計數,以在第二次刪除作業時正確刪除引擎。

程式設計注意事項

OpenSL ES 程式設計注意事項提供了補充資訊,以確保可以正確實作 OpenSL ES。

注意:為了方便起見,我們在 docs/opensles/OpenSL_ES_Specification_1.0.1.pdf 中隨 NDK 一併提供了 OpenSL ES 1.0.1 規格。

平台問題

本節說明在支援這些 API 的初始平台版本中已知的問題。

動態介面管理

DynamicInterfaceManagement::AddInterface 無法運作。請改為在傳遞到 Create() 的陣列中指定介面,如環境回響的範例程式碼所示。

規劃未來版本的 OpenSL ES

Android 高效能音訊 API 以 Khronos Group OpenSL ES 1.0.1 為基礎。Khronos 已發布此標準版本的 1.1 修訂版本。修訂版本包含新功能和澄清說明,並校正了排版錯誤及部分不相容問題。大多數預期的不相容問題都相對輕微,或屬於 Android 不支援的 OpenSL ES 範疇。

只要遵循下方「二進位檔相容性規畫」一節所列出的準則,使用此版本開發的應用程式應該也能在日後推出的 Android 平台版本上執行。

注意:我們並未以與未來的原始碼相容為目標。也就是說,如果您升級至新版 NDK,則可能需要修改應用程式原始碼,以符合新版 API 的要求。我們預計這類變更大多是輕微修改,詳情請見下文。

二進位檔相容性規畫

建議您讓應用程式遵守以下準則,提升日後的二進位檔相容性:

  • 僅使用 Android 支援的 OpenSL ES 1.0.1 功能中已有記錄的部分功能。
  • 不要依賴特定結果程式碼來瞭解失敗作業,做好處理其他結果程式碼的準備。
  • 應用程式回呼處理常式通常會在受限制的環境中執行。編寫的應用程式回呼處理常式應該能夠快速處理工作,並盡快傳回。請勿在回呼處理常式中執行複雜作業。例如,在緩衝區佇列完成回呼中,可以將另一個緩衝區排入佇列,但不要建立音訊播放器。
  • 回呼處理常式應準備好進行不同頻率的呼叫,以及接收其他事件類型,並且應該忽略無法辨識的事件類型。設定了事件遮罩 (由已啟用的事件類型組成) 的回呼應做好準備,支援在同時設定多個事件類型位元的情況下受到呼叫。請使用「&」(而非「switch case」) 來測試每個事件位元。
  • 使用預先擷取狀態和回呼做為一般進度指示,但不要依賴特定的硬式編碼填充等級或回呼序列。預先擷取狀態填充等級的含義,以及在預先擷取期間偵測到的錯誤行為可能會有所變化。

注意:詳情請參閱下方「緩衝區佇列行為」一節。

原始碼相容性規畫

如前文所述,Khronos Group 開發的下一版 OpenSL ES 預計會出現原始碼不相容問題。可能會出現以下方面的變動:

  • 緩衝區佇列介面預計會有重大變動,尤其是在 BufferQueue::EnqueueslBufferQueueCallback 參數清單及 SLBufferQueueState.playIndex 欄位名稱等方面。建議您將應用程式程式碼改用 Android 簡單緩衝區佇列。出於這個原因,在 NDK 隨附的範例程式碼中,我們已針對播放使用 Android 簡單緩衝區佇列。我們還使用 Android 簡單緩衝區佇列進行錄製和 PCM 解碼,不過這是因為標準 OpenSL ES 1.0.1 不支援錄製或解碼為緩衝區佇列資料接收器。
  • 將會向由參照傳遞的輸入參數及做為輸入值的 SLchar * 結構欄位增加 const。您無需為此變更程式碼。
  • 一些目前有正負號的參數將替換成無正負號的類型。您可能需要將參數類型從 SLint32 改為 SLuint32 或類似類型,或者新增類型轉換。
  • Equalizer::GetPresetName 會將字串複製到應用程式記憶體,而不是傳回指向實作記憶體的指標。這將是一項重大變更,因此建議您避免呼叫此方法,或在隔離狀態下使用此方法。
  • 在結構類型中會有額外欄位。對於輸出參數,可以忽略這些新欄位;但對於輸入參數,則需要初始化新欄位。幸運的是,所有額外欄位預計都屬於 Android 不支援的領域。
  • 介面 GUID 將會變更。請透過符號名稱 (而非 GUID) 參照介面,藉此避免依賴。
  • SLchar 將從 unsigned char 變更為 char。這項變更主要會影響 URI 資料定位器和 MIME 資料格式。
  • SLDataFormat_MIME.mimeType 將重新命名為 pMimeType,而 SLDataLocator_URI.URI 將重新命名為 pURI。建議您使用以半形大括號括住並以半形逗號分隔的值清單 (而非欄位名稱) 來初始化 SLDataFormat_MIMESLDataLocator_URI,藉此確保您的程式碼不受此變更影響。範例程式碼就是使用了這種方法。
  • SL_DATAFORMAT_PCM 不允許應用程式將資料的表示方式指定為有符號的整數、無符號的整數或浮點。Android 實作會假設 8 位元資料是無符號整數,而且 16 位元資料則是有符號整數。此外,samplesPerSec 欄位的名稱也不恰當,因為實際單位為 milliHz。我們預計將在下一版 OpenSL ES 中解決上述問題。新版本將提供新的擴充 PCM 資料格式,允許應用程式明確指定表示方法,並且會更正欄位名稱。由於這是新的資料格式,而目前的 PCM 資料格式仍可繼續使用 (雖然已淘汰),因此您不需要立即變更程式碼。