Android 擴充功能

適用於 Android 的 OpenSL ES 擴展了 OpenSL ES 的參考規範,使其與 Android 相容,並充分運用 Android 平台的強大功能與靈活性。

如需有關 Android 擴充功能的 API 定義,請參見 OpenSLES_Android.h 及其中的標頭檔案;如要進一步瞭解這些擴充功能,請參閱 OpenSLES_Android.h。這個檔案位於安裝根目錄下的 sysroot/usr/include/SLES 目錄中。除非另有註明,否則所有介面皆為明確介面。

這些擴充功能會限制將應用程式移植至其他 OpenSL ES 實作的可攜性,因為這些擴充功能僅適用於 Android。如要緩解這個問題,您可以避免使用擴充功能,或是在編譯時使用 #ifdef 排除擴充功能。

下表列出 Android OpenSL ES 針對各種物件類型支援的 Android 專屬介面和資料定位器。如果儲存格中的值為「是」,代表相應的介面和資料定位器可用於該物件類型。

功能 音訊播放器 錄音工具 引擎 混音輸出
Android 緩衝區佇列 是:來源 (解碼) 不可以
Android 設定
Android 效果
Android 效果功能
Android 效果傳送 不可以
Android 簡單緩衝區佇列 是:來源 (播放) 或接收器 (解碼)
Android 緩衝區佇列資料定位器 是:來源 (解碼) 不可以
Android 檔案描述元資料定位器 是:來源 不可以
Android 簡單緩衝區佇列資料定位器 是:來源 (播放) 或接收器 (解碼) 是:接收器

Android 設定介面

Android 設定介面可讓您為物件設定平台專屬參數。這個介面不同於其他 OpenSL ES 1.0.1 介面,可讓您的應用程式先使用此介面,再針對對應的物件執行個體化;因此,您可以先設定物件,然後再執行個體化。/sysroot/usr/include/SLES 中的 OpenSLES_AndroidConfiguration.h 標頭檔案記錄了以下可用的設定鍵和值:

  • 音訊播放器的串流類型 (預設為 SL_ANDROID_STREAM_MEDIA)。
  • 錄音工具的錄音設定檔 (預設為 SL_ANDROID_RECORDING_PRESET_GENERIC)。

以下程式碼片段範例說明如何在音訊播放器上設定 Android 音訊串流類型:

// CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION
// in the required interface ID array. Do not realize player yet.
// ...
SLAndroidConfigurationItf playerConfig;
result = (*playerObject)->GetInterface(playerObject,
    SL_IID_ANDROIDCONFIGURATION, &playerConfig);
assert(SL_RESULT_SUCCESS == result);
SLint32 streamType = SL_ANDROID_STREAM_ALARM;
result = (*playerConfig)->SetConfiguration(playerConfig,
    SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
assert(SL_RESULT_SUCCESS == result);
// ...
// Now realize the player here.

您可使用類似的程式碼設定錄音工具的預設值:

// ... obtain the configuration interface as the first four lines above, then:
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
result = (*playerConfig)->SetConfiguration(playerConfig,
    RECORDING_PRESET, &presetValue, sizeof(SLuint32));

Android 效果介面

Android 的效果、效果傳送和效果功能介面針對應用程式提供通用機制,可供應用程式查詢及使用裝置專屬的音效。裝置製造商應在文件中記錄所提供的任何裝置專屬音效。

可攜式應用程式應使用 OpenSL ES 1.0.1 API 提供音效,而非使用 Android 效果擴充功能。

Android 檔案描述元資料定位器

透過 Android 檔案描述元資料定位器,您可將音訊播放器的來源指定為具有讀取存取權的開放式檔案描述元。資料格式必須為 MIME。

由於應用程式可透過檔案描述元從 APK 讀取資產,因此這項擴充功能特別適合搭配原生資產管理工具。

Android 簡單緩衝區佇列資料定位器和介面

在 OpenSL ES 1.0.1 參考規格中,緩衝區佇列只能用於音訊播放器,並與 PCM 和其他資料格式相容。Android 簡單緩衝區佇列資料定位器和介面規格與參考規格相同,但有以下兩個例外狀況:

  • 您可以將 Android 簡單緩衝區佇列和錄音工具及音訊播放器搭配使用。
  • 這類佇列只能使用 PCM 資料格式。

如要錄製,應用程式必須將空緩衝區排入佇列。當註冊的回呼傳送通知,表示系統已將資料寫入緩衝區時,應用程式就能從該緩衝區讀取資料。

播放功能的運作方式與此相同。不過,為了在日後維持原始碼相容性,我們建議應用程式使用 Android 簡單緩衝區佇列,而非 OpenSL ES 1.0.1 緩衝區佇列。

緩衝區佇列行為

參考規範要求當播放進入 SL_PLAYSTATE_STOPPED 狀態時,播放游標應回到目前播放緩衝區的開頭,而 Android 實作沒有滿足這個要求。此實作項目可遵守該行為要求,也可以保持播放游標位置不變。因此,您的應用程式無法推測哪一項行為會發生。這表示您必須在轉換至 SL_PLAYSTATE_STOPPED 後明確地呼叫 BufferQueue::Clear() 方法。這麼做可將緩衝區佇列設為已知狀態。

同樣地,沒有任何規範針對緩衝區佇列回呼規定觸發條件必須是轉換至 SL_PLAYSTATE_STOPPED 還是執行 BufferQueue::Clear()。因此,建議您不要為任一行為建立依附元件,應用程式應可處理這兩種情況。

建立物件時使用動態介面

為方便起見,應用程式可透過 OpenSL ES 1.0.1 的 Android 實作項目,在將物件例項化時指定動態介面。這是建立例項之後,使用 DynamicInterfaceManagement::AddInterface() 新增介面的替代方法。

報告擴充功能

如要查詢平台是否支援 Android 擴充功能,有三種方法可供選擇,列舉如下:

  • Engine::QueryNumSupportedExtensions()
  • Engine::QuerySupportedExtension()
  • Engine::IsExtensionSupported()

這些方法都會傳回 ANDROID_SDK_LEVEL_<API-level>,其中 API-level 是平台 API 級別,如 ANDROID_SDK_LEVEL_23。平台 API 級別為 9 以上時,表示平台支援擴充功能。

將音訊解碼為 PCM

本節說明 OpenSL ES 1.0.1 中已淘汰的 Android 專用擴充功能,此擴充功能可將經過編碼的串流解碼為 PCM,而不立即播放。下表列出這項擴充功能與替代選項的使用建議。

API 級別 替代選項
15 及更低級別 具備適當授權的開放原始碼轉碼器
16 至 20 MediaCodec 類別或具備適當授權的開放原始碼轉碼器
21 及更高級別 <media/NdkMedia*.h> 標頭檔案中的 NDK MediaCodec、MediaCodec 類別,或具備適當授權的開放原始碼轉碼器

注意:目前沒有 MediaCodec API 的 NDK 版本文件。不過如需範例,可以參照 native-codec 程式碼範例。

標準音訊播放器可以在音訊裝置上播放,並將混音輸出指定為資料接收器。Android 擴充功能的差別之處在於,如果應用程式已將資料來源指定為 URI,或者是使用 MIME 資料格式描述的 Android 檔案描述元資料定位器,則音訊播放器會是解碼器。在這類情況下,資料接收器是採用 PCM 資料格式的 Android 簡單緩衝區佇列資料定位器。

這項功能主要是讓遊戲在切換至新關卡時,預先載入音訊資產,這類似於 SoundPool 類別所提供的功能。

應用程式最初應會將一組 Android 簡單緩衝區佇列中的空緩衝區排入佇列。之後,應用程式會在緩衝區中填入 PCM 資料。每個緩衝區都填入完畢後,Android 簡單緩衝區佇列回呼就會觸發。回呼處理常式會處理 PCM 資料並將目前的空緩衝區重新排入佇列,然後再傳回。應用程式負責追蹤已解碼的緩衝區;回呼參數清單的資訊不足,無法指出含有資料的緩衝區或應排入佇列的下一個緩衝區。

資料來源會在資料流結束時傳送 SL_PLAYEVENT_HEADATEND 事件以隱式回報資料流結尾 (EOS)。應用程式解碼所有收到的資料後,就不會再對 Android 簡單緩衝區佇列回呼發出呼叫。

在取樣率、聲道數和位元深度方面,接收器的 PCM 資料格式通常與編碼的資料來源相符。不過,您可以解碼為不同的取樣率、聲道數或位元深度。如需用來偵測實際 PCM 格式的佈建資訊,請參閱「透過中繼資料判斷已解碼的 PCM 資料格式」。

Android 版 OpenSL ES 的 PCM 解碼功能支援暫停和初始跳轉功能,但不支援音量控制、效果、循環播放功能,也無法調整播放速率。

取決於平台實作,要進行解碼時可能會要求資源不能處於閒置狀態。因此,建議您確定提供了足夠的空 PCM 緩衝區,否則會導致解碼器飢餓。例如,如果您的應用程式從 Android 簡單緩衝區佇列回呼傳回,但沒有將另一個空緩衝區排入佇列,就可能會發生這種情況。我們無法指定解碼器飢餓的結果,但可能包含:丟棄已解碼的 PCM 資料、暫停解碼程序,或立即終止解碼器。

注意:如要將經過編碼的串流解碼為 PCM,但不立即播放,則若是 Android 4.x (API 級別 16 至 20) 上執行的應用程式,建議您使用 MediaCodec 類別。如果是在 Android 5.0 (API 級別 21) 或以上版本上執行的新應用程式,則建議使用與 NDK 相等的 <NdkMedia*.h>。這些標頭檔案位於安裝根目錄下的 media/ 目錄中。

將 ADTS AAC 串流解碼為 PCM

如果資料來源是採用 MIME 資料格式的 Android 緩衝區佇列資料定位器,而資料接收器是採用 PCM 資料格式的 Android 簡單緩衝區佇列資料定位器,則音訊播放器會是串流解碼器。請按照下列方式設定 MIME 資料格式:

  • 容器:SL_CONTAINERTYPE_RAW
  • MIME 類型字串:SL_ANDROID_MIME_AACADTS

這項功能主要適用於處理 AAC 音訊,但必須在播放前執行自訂音訊處理作業的串流媒體應用程式。需要將音訊解碼為 PCM 的大多數應用程式都應使用將音訊解碼為 PCM 中所述的方法,因為這個方法比較簡單,又能處理更多音訊格式。此處介紹的技術是較專業的方法,只能用在滿足以下兩項條件的情況:

  • 經過壓縮的音訊來源是 ADTS 標頭中的 AAC 影格串流。
  • 應用程式管理這個串流。資料「並非」位於 ID 為 URI 的網路資源,也不位於 ID 為檔案描述元的本機檔案中。

一開始,應用程式應將 Android 緩衝區佇列中一組已填滿的緩衝區排入佇列。每個緩衝區都包含一或多個完整的 ADTS AAC 影格。Android 緩衝區佇列回呼會在每個緩衝區清空後觸發。回呼處理常式應重新填入並重新排入緩衝區,然後再傳回。應用程式不需追蹤已編碼的緩衝區;回呼參數清單包含充足的資訊,能指出應排入佇列的下一個緩衝區。串流結尾會透過將 EOS 項目排入佇列,加上明確標記。在 EOS 之後,就無法將更多內容排入佇列。

因此,建議您確定提供了填滿的 ADTS AAC 緩衝區,以免發生解碼器飢餓的情況。舉例來說,如果應用程式從 Android 緩衝區佇列回呼傳回,但未將另一個填滿的緩衝區排入佇列,就可能發生這種情況。我們無法指定解碼器飢餓的結果。

除了資料來源以外,串流解碼方法的各方面皆與「將音訊解碼為 PCM」一節所述的方法相同。

儘管名稱相似,但 Android 緩衝區佇列和 Android 簡單緩衝區佇列並「不相同」。串流解碼器會使用上述兩種緩衝佇列:Android 緩衝區佇列用於 ADTS AAC 資料來源,Android 簡單緩衝區佇列則用於 PCM 資料接收器。如要進一步瞭解 Android 簡單緩衝區佇列 API,請參閱「Android 簡單緩衝區佇列資料定位器和介面」。如要進一步瞭解 Android 緩衝區佇列 API,請參閱安裝根目錄下 docs/Additional_library_docs/openmaxal/ 目錄中的 index.html 檔案。

透過中繼資料決定已解碼的 PCM 資料格式

SLMetadataExtractionItf 介面是參考規格的一部分。不過,用來表示已解碼 PCM 資料實際格式的中繼資料索引鍵為 Android 專用。OpenSLES_AndroidMetadata.h 標頭檔案會定義這些中繼資料索引鍵。這個標頭檔案位於安裝根目錄下的 /sysroot/usr/include/SLES 目錄中。

Object::Realize() 方法執行完畢後,便立即可使用中繼資料索引鍵。不過,必須在應用程式將第一筆編碼資料解碼後,才能使用相關值。建議的做法是在呼叫 Object::Realize 方法後在主執行緒中查詢索引鍵,以及在首次呼叫時讀取 Android 簡單緩衝區佇列回呼處理常式中的 PCM 格式中繼資料值。如要瞭解可使用這個介面的程式碼,請參閱 NDK 套件的範例程式碼

中繼資料索引鍵名稱保持不變,但索引鍵並未記錄,而且可能會變更。應用程式不應假設在不同執行作業期間,索引會一直存在,也不應假設在相同執行作業中,多個物件例項會共用索引。

浮點資料

在 Android 5.0 (API 級別 21) 以上版本執行的應用程式,能為 AudioPlayer 提供單精度浮點格式資料。

在下方的範例程式碼中,Engine::CreateAudioPlayer() 方法會建立使用浮點資料的音訊播放器:

#include <SLES/OpenSLES_Android.h>
...
SLAndroidDataFormat_PCM_EX pcm;
pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
pcm.numChannels = 2;
pcm.sampleRate = SL_SAMPLINGRATE_44_1;
pcm.bitsPerSample = 32;
pcm.containerSize = 32;
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
...
SLDataSource audiosrc;
audiosrc.pLocator = ...
audiosrc.pFormat = &pcm;
敬上 進一步瞭解浮點音訊 音訊取樣。