適用于Android的OpenSL ES指南-OpenSL ES的Android擴展

翻譯自Android Extensions

針對Android的OpenSL ES擴展了參考OpenSL ES規范,使其與Android兼容,并利用Android平臺的強大功能和靈活性。

Android擴展的API定義在OpenSLES_Android.h和它包含的頭文件中。查閱OpenSLES_Android.h了解這些擴展的詳細信息。這個文件位于您的安裝根目錄下,在sysroot/usr/include/SLES目錄下。除非另有說明,所有接口都是顯式的。

這些擴展限制了應用程序到其他OpenSL ES實現的可移植性,因為它們是特定于android的。您可以通過避免使用擴展或使用#ifdef在編譯時排除它們來緩解這個問題。

下表顯示了Android特定的接口和數據定位器及Android OpenSL ES支持的每種對象類型。單元格中的Yes值表示對象類型都可用的接口和數據定位器data locators。

Feature Audio player Audio recorder Engine Output mix
Android buffer queue Yes: Source (decode) No No No
Android configuration Yes Yes No No
Android effect Yes No No Yes
Android effect capabilities No No Yes No
Android effect send Yes No No No
Android simple buffer queue Yes: Source (playback) or sink (decode) Yes No No
Android buffer queue data locator Yes: Source (decode) No No No
Android file descriptor data locator Yes: Source No No No
Android simple buffer queue data locator Yes: Source (playback) or sink (decode) Yes: Sink No No

Android配置接口

Android配置界面提供了一種為對象設置特定于平臺的參數的方法。該接口不同于其他OpenSL ES 1.0.1接口,因為您的應用程序可以在實例化相應對象之前使用它;而且,您可以在實例化對象之前配置它。OpenSLES_AndroidConfiguration.h頭文件,駐留在/sysroot/usr/include/SLES中,記錄了以下可用的配置鍵值:

  • 音頻播放器的流類型(默認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 effects interfaces 效果接口

Android的效果、效果發送和效果功能接口為應用程序查詢和使用特定于設備的音頻效果提供了通用機制。設備制造商應該記錄他們提供的任一設備特定的音頻效果。

便攜應用應該使用OpenSL ES 1.0.1 api來實現音頻效果,而不是Android效果擴展。

Android file descriptor data locator 文件描述符數據定位器

Android文件描述符數據定位器允許您將音頻播放器的源指定為具有讀權限的開放文件描述符。數據格式必須是MIME。

這個擴展與native asset manager結合使用特別有用,因為應用程序通過文件描述符從APK讀取assets。

Android simple buffer queue data locator and interface 簡單的緩沖隊列數據定位器和接口

在OpenSL ES 1.0.1參考規范中,緩沖區隊列只能用于音頻播放器,它們與PCM和其他數據格式兼容。Android簡單緩沖隊列數據定位器和接口規范與參考規范相同,但有兩個例外:

  • 您可以使用帶有錄音機和音頻播放器的Android簡單緩沖隊列。
  • 您只能對這些隊列使用PCM數據格式。

為了記錄,您的應用程序應該排隊空緩沖區。當一個注冊回調發送一個通知,系統已經寫完數據到緩沖區,應用程序可以從緩沖區讀取。

回放以同樣的方式工作。但是,為了將來的源代碼兼容性,我們建議應用程序使用Android簡單緩沖區隊列,而不是OpenSL ES 1.0.1緩沖區隊列。

buffer queue緩沖隊列行為

Android實現不包括引用規范的要求,即當回放進入SL_PLAYSTATE_STOPPED狀態時,播放cursor返回到當前播放緩沖區的開始位置。該實現可以順應該行為,也可以保持播放cursor的位置不變。因此,您的應用程序不能假定這兩種行為都發生了。因此,在轉換到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 level Alternatives替代方案
15 及以下 一種具有適當許可證的開源編解碼器
16 到 20 MediaCodec類或具有適當許可證的開源編解碼器
21 以上 NDK MediaCodec在<media/NdkMedia*.>頭文件,MediaCodec類,或具有適當許可證的開源編解碼器

注意:目前沒有關于MediaCodec API的NDK版本的文檔。但是,您可以參考natvie-codec示例代碼。

標準音頻播放器回放音頻設備,指定輸出混合作為數據接收器。Android擴展的不同之處在于,如果應用程序將數據源指定為URI或使用MIME數據格式描述的Android文件描述符數據定位器,那么音頻播放器將充當解碼器。在這種情況下,數據接收器是一個使用PCM數據格式的Android簡單緩沖隊列數據定位器。

這個特性主要用于游戲在轉換到新的游戲級別時預加載它們的音頻assets,這與SoundPool類提供的功能類似。

應用程序最初應該在Android簡單緩沖區隊列中加入一組空緩沖區。然后,應用程序用PCM數據填充緩沖區。Android簡單的緩沖區隊列回調在每個緩沖區被填滿后觸發?;卣{處理程序處理PCM數據,重新裝入現在為空的緩沖區,然后返回。應用程序負責跟蹤解碼緩沖區(buffer);回調參數列表不包含足夠的信息來指示包含數據的緩沖區或接下來應該加入隊列的緩沖區。

數據源通過在流的末尾傳遞SL_PLAYEVENT_HEADATEND事件隱式地報告流的結束(EOS)。當應用程序解碼了它接收到的所有數據后,它就不再調用Android簡單緩沖隊列回調。

數據槽sink的PCM數據格式通常與編碼的數據源在采樣率、聲道數和位深度方面匹配。但是,您可以解碼到不同的采樣率、聲道數或位深度。有關檢測實際PCM格式的規定的信息,請看下面的通過元數據確定解碼PCM數據的格式部分

OpenSL ES為Android的PCM解碼功能,支持暫停和初始搜索;它不支持音量控制、效果、循環或播放速率。

根據平臺實現的不同,解碼可能需要不能閑置的資源。因此,我們建議您確保提供足夠數量的空PCM緩沖區;否則,解碼器將挨餓。這可能發生,例如,如果您的應用程序從Android簡單的緩沖區隊列回調返回,而不排隊另一個空緩沖區。解碼器餓死的結果是未知的,但可能包括:刪除解碼PCM數據,暫停解碼過程,或徹底終止解碼器。

注意:對于運行在Android 4.x (API級別16-20)上的應用程序,解碼一個編碼流到PCM,但不立即回放,我們建議使用MediaCodec類。對于在Android 5.0 (API level 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幀流。
  • 應用程序管理這個流。數據不位于標識符為URI的網絡資源中,也不位于標識符為文件描述符的本地文件中。

應用程序最初應該在Android緩沖區隊列中加入一組已填充的緩沖區。每個緩沖區包含一個或多個完整的ADTS AAC幀。每個緩沖區清空后,Android緩沖區隊列回調會觸發?;卣{處理程序應該重新填充緩沖區并重新排隊,然后返回。應用程序不需要跟蹤已編碼的緩沖區;回調參數列表包含足夠的信息來指示下一個應該加入隊列的緩沖區。流的末尾通過對EOS項進行排隊顯式地標記。EOS后,不允許再排隊。

我們建議您確保提供完整的ADTS AAC緩沖區,以避免使解碼器挨餓。這可能發生,例如,如果您的應用程序從Android buffer queue回調返回,而不排隊另一個完整的緩沖區。解碼器餓死的結果是未知的。

除了數據源之外,流解碼方法與解碼音頻到PCM的方法相同。

雖然名稱相似,但Android緩沖隊列與Android簡單緩沖隊列不同。流解碼器使用兩個緩沖隊列:ADTS AAC數據源的Android緩沖隊列和PCM數據槽的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 level 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;

在音頻采樣頁上閱讀關于浮點音頻的更多信息。


下一篇: 適用于android的OpenSL ES指南-編程注意事項

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容