AAudio 是 Android 8.0 版本中推出的音訊 API,Android 8.1 版本經過強化,可與支援 MMAP 的 HAL 和驅動程式搭配使用,從而縮短延遲時間。本文說明在 Android 中支援 AAudio 的 MMAP 功能所需的硬體抽象層 (HAL) 和驅動程式變更。
如要支援 AAudio MMAP,您必須:
- 回報 HAL 的 MMAP 功能
- 在 HAL 中實作新函式
- 可選擇為 EXCLUSIVE 模式緩衝區實作自訂 ioctl()
- 提供額外的硬體資料路徑
- 設定啟用 MMAP 功能的系統屬性
AAudio 架構
AAudio 是新的原生 C API,可做為 Open SL ES 的替代方案。它會使用建構工具設計模式建立音訊串流。
AAudio 提供低延遲資料路徑。在 EXCLUSIVE 模式中,這項功能可讓用戶端應用程式程式碼直接寫入與 ALSA 驅動程式共用的記憶體對應緩衝區。在共用模式中,AudioServer 中執行的調節器會使用 MMAP 緩衝區。在 EXCLUSIVE 模式中,由於資料會略過混合器,因此延遲時間會大幅減少。
在 EXCLUSIVE 模式中,服務會向 HAL 要求 MMAP 緩衝區,並管理資源。MMAP 緩衝區會在 NOIRQ 模式下執行,因此沒有共用讀取/寫入計數器可管理緩衝區存取權。而是由用戶端維護硬體的時間模型,並預測緩衝區何時會讀取。
在下圖中,我們可以看到脈衝編碼調變 (PCM) 資料透過 MMAP FIFO 流向 ALSA 驅動程式。AAudio 服務會定期要求時間戳記,然後透過原子訊息佇列將其傳遞至用戶端的時間控管模型。

在共用模式中,也會使用時間模型,但該模型位於 AAudioService 中。
對於音訊擷取,則會使用類似的模型,但 PCM 資料會以相反方向流動。
HAL 變更
如要瞭解 tinyALSA,請參閱:
external/tinyalsa/include/tinyalsa/asoundlib.h external/tinyalsa/include/tinyalsa/pcm.c
int pcm_start(struct pcm *pcm); int pcm_stop(struct pcm *pcm); int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, unsigned int *frames); int pcm_get_poll_fd(struct pcm *pcm); int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames); int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
如需舊版 HAL,請參閱:
hardware/libhardware/include/hardware/audio.h hardware/qcom/audio/hal/audio_hw.c
int start(const struct audio_stream_out* stream); int stop(const struct audio_stream_out* stream); int create_mmap_buffer(const struct audio_stream_out *stream, int32_t min_size_frames, struct audio_mmap_buffer_info *info); int get_mmap_position(const struct audio_stream_out *stream, struct audio_mmap_position *position);
針對 HIDL 音訊 HAL:
hardware/interfaces/audio/2.0/IStream.hal hardware/interfaces/audio/2.0/types.hal hardware/interfaces/audio/2.0/default/Stream.h
start() generates (Result retval); stop() generates (Result retval) ; createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info); getMmapPosition() generates (Result retval, MmapPosition position);
回報 MMAP 支援
系統屬性「aaudio.mmap_policy」應設為 2 (AAUDIO_POLICY_AUTO),讓音訊架構知道音訊 HAL 支援 MMAP 模式。(請參閱下方的「啟用 AAudio MMAP 資料路徑」)。
audio_policy_configuration.xml 檔案也必須包含專屬於 MMAP/NO IRQ 模式的輸出和輸入設定檔,以便 Audio Policy Manager 在建立 MMAP 用戶端時,知道要開啟哪個串流:
<mixPort name="mmap_no_irq_out" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </mixPort> <mixPort name="mmap_no_irq_in" role="sink" flags="AUDIO_INPUT_FLAG_MMAP_NOIRQ"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/> </mixPort>
開啟及關閉 MMAP 串流
createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info);
您可以呼叫 Tinyalsa 函式來開啟及關閉 MMAP 串流。
查詢 MMAP 位置
傳回至時間設定模型的時間戳記包含影格位置和以奈秒為單位的 MONOTONIC 時間:
getMmapPosition() generates (Result retval, MmapPosition position);
HAL 可以呼叫新的 Tinyalsa 函式,從 ALSA 驅動程式取得這項資訊:
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
共用記憶體的檔案描述元
AAudio MMAP 資料路徑會使用硬體和音訊服務之間共用的記憶體區域。系統會使用由 ALSA 驅動程式產生的檔案描述元參照共用記憶體。
核心變更
如果檔案描述項直接與 /dev/snd/
驅動程式檔案相關聯,AAudio 服務就能在共用模式下使用該檔案描述項。不過,描述符無法傳遞至用於 EXCLUSIVE 模式的用戶端程式碼。/dev/snd/
檔案描述元會為用戶端提供過於廣泛的存取權,因此遭到 SELinux 封鎖。
為了支援 EXCLUSIVE 模式,您必須將 /dev/snd/
描述元轉換為 anon_inode:dmabuf
檔案描述元。SELinux 會允許將該檔案描述元傳送至用戶端。這項功能也能由 AAudioService 使用。
您可以使用 Android Ion 記憶體程式庫產生 anon_inode:dmabuf
檔案描述元。
如需更多資訊,請參閱下列外部資源:
- 「Android ION 記憶體配置器」https://lwn.net/Articles/480055/
- 「Android ION 總覽」https://wiki.linaro.org/BenjaminGaignard/ion
- 「Integrating the ION memory allocator」(整合 ION 記憶體配置器) https://lwn.net/Articles/565469/
HAL 變更
AAudio 服務需要知道是否支援這個 anon_inode:dmabuf
。在 Android 10.0 之前,唯一可行的方法是將 MMAP 緩衝區的大小設為負數,例如 -2048 而非 2048 (如果支援)。在 Android 10.0 以上版本中,您可以設定 AUDIO_MMAP_APPLICATION_SHAREABLE
標記。
mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;
音訊子系統變更
AAudio 需要在音訊子系統的音訊前端提供額外資料路徑,才能與原始 AudioFlinger 路徑並行運作。這個舊版路徑會用於所有其他系統音效和應用程式音效。這項功能可由 DSP 中的軟體混合器或 SOC 中的硬體混合器提供。
啟用 AAudio MMAP 資料路徑
如果不支援 MMAP 或無法開啟串流,AAudio 會使用舊版 AudioFlinger 資料路徑。因此,AAudio 會與不支援 MMAP/NOIRQ 路徑的音訊裝置搭配使用。
測試 AAudio 的 MMAP 支援功能時,請務必瞭解您實際上是測試 MMAP 資料路徑,還是僅測試舊版資料路徑。以下說明如何啟用或強制特定資料路徑,以及如何查詢串流使用的路徑。
系統屬性
您可以透過系統屬性設定 MMAP 政策:
- 1 = AAUDIO_POLICY_NEVER - 僅使用舊版路徑。請勿嘗試使用 MMAP。
- 2 = AAUDIO_POLICY_AUTO - 嘗試使用 MMAP。如果失敗或無法使用,請使用舊版路徑。
- 3 = AAUDIO_POLICY_ALWAYS - 僅使用 MMAP 路徑。請勿改用舊版路徑。
這些值可在裝置的 Makefile 中設定,如下所示:
# Enable AAudio MMAP/NOIRQ data path. # 2 is AAUDIO_POLICY_AUTO so it will try MMAP then fallback to Legacy path. PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_policy=2 # Allow EXCLUSIVE then fall back to SHARED. PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_exclusive_policy=2
您也可以在裝置啟動後覆寫這些值。您必須重新啟動音訊伺服器,變更才會生效。舉例來說,如要為 MMAP 啟用 AUTO 模式:
adb root
adb shell setprop aaudio.mmap_policy 2
adb shell killall audioserver
ndk/sysroot/usr/include/aaudio/AAudioTesting.h
中提供的函式可讓您覆寫使用 MMAP 路徑的政策:
aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);
如要瞭解串流是否使用 MMAP 路徑,請呼叫:
bool AAudioStream_isMMapUsed(AAudioStream* stream);