適用於Android的OpenSL ES指南-OpenSL ES的Android擴充套件

DamonRen發表於2019-02-19

翻譯自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指南-程式設計注意事項

相關文章