ALSA 壓縮解除安裝 API

Pierre-Louis.Bossart <pierre-louis.bossart@linux.intel.com>

Vinod Koul <vinod.koul@linux.intel.com>

概述

自早期以來,ALSA API 的定義就著眼於 PCM 支援或恆定位元率有效載荷,例如 IEC61937。幀中的引數和返回值是常態,這使得擴充套件現有 API 以支援壓縮資料流具有挑戰性。

近年來,音訊數字訊號處理器 (DSP) 整合到了片上系統設計中,並且 DSP 也整合到了音訊編解碼器中。與基於主機的處理相比,在此類 DSP 上處理壓縮資料可以顯著降低功耗。 Linux 對此類硬體的支援一直不太好,主要是因為主線核心中缺少通用的 API。

與其透過更改 ALSA PCM 介面的 API 來打破相容性,不如引入一個新的“壓縮資料”API,為音訊 DSP 提供控制和資料流介面。

此 API 的設計靈感來自 Intel Moorestown SOC 的 2 年經驗,需要進行許多更正才能將 API 上游到主線核心,而不是分段樹,並使其可供其他人使用。

要求

主要要求是

  • 位元組計數和時間之間的分離。壓縮格式可能具有每個檔案、每個幀的標頭,或者根本沒有標頭。有效載荷大小可能因幀而異。 因此,在處理壓縮資料時,無法可靠地估計音訊緩衝區的持續時間。 需要專門的機制來實現可靠的音影片同步,這需要準確報告在任何給定時間呈現的樣本數量。

  • 處理多種格式。 PCM 資料只需要指定取樣率、通道數和每個樣本的位數。 相比之下,壓縮資料有多種格式。 音訊 DSP 還可以透過韌體中嵌入的有限數量的音訊編碼器和解碼器提供支援,或者可以透過動態下載庫來支援更多選擇。

  • 關注主要格式。 此 API 提供對用於音訊和影片捕獲和播放的最流行格式的支援。 隨著音訊壓縮技術的進步,可能會新增新的格式。

  • 處理多種配置。 即使對於給定的格式(如 AAC),某些實現也可能支援 AAC 多聲道,但支援 HE-AAC 立體聲。 同樣,WMA10 M3 級別可能需要太多的記憶體和 CPU 週期。 新的 API 需要提供一種通用的方式來列出這些格式。

  • 僅渲染/抓取。 此 API 不提供任何硬體加速的方式,在這種加速中,PCM 樣本被提供回用戶空間以進行額外的處理。 相反,此 API 側重於將壓縮資料流式傳輸到 DSP,並假設解碼後的樣本被路由到物理輸出或邏輯後端。

  • 隱藏複雜性。 現有的使用者空間多媒體框架都為每種壓縮格式提供了現有的列舉/結構。 這個新的 API 假定存在一個平臺特定的相容層來公開、轉換和利用音訊 DSP 的功能,例如 Android HAL 或 PulseAudio sinks。 透過構造,常規應用程式不應該使用此 API。

設計

新的 API 與 PCM API 在流量控制方面共享許多概念。 啟動、暫停、恢復、排空和停止命令具有相同的語義,無論內容是什麼。

記憶體環形緩衝區劃分為一組片段的概念是從 ALSA PCM API 借用的。 但是,只能指定位元組大小。

搜尋/技巧模式被假定由主機處理。

不支援倒帶/快進的概念。 提交到環形緩衝區的資料無法失效,除非刪除所有緩衝區。

壓縮資料 API 不對資料如何傳輸到音訊 DSP 做任何假設。 從主記憶體到嵌入式音訊叢集的 DMA 傳輸或到外部 DSP 的 SPI 介面是可能的。 與 ALSA PCM 情況一樣,公開了一組核心例程; 每個驅動程式實現者都必須編寫對一組強制性例程的支援,並且可能需要使用可選的例程。

主要的補充是

get_caps

此例程返回支援的音訊格式列表。 查詢捕獲流上的編解碼器將返回編碼器,而解碼器將列出用於播放流。

get_codec_caps

對於每個編解碼器,此例程返回一個功能列表。 目的是確保所有功能都對應於有效的設定,並最大限度地減少配置失敗的風險。 例如,對於像 AAC 這樣複雜的編解碼器,支援的通道數可能取決於特定的配置檔案。 如果這些功能是用單個描述符公開的,則可能會出現特定的配置檔案/通道/格式組合不受支援的情況。 同樣,嵌入式 DSP 的記憶體和 CPU 週期有限,某些實現可能會使功能列表動態化並依賴於現有的工作負載。 除了編解碼器設定外,此例程還會返回實現處理的最小緩衝區大小。 此資訊可以是 DMA 緩衝區大小、同步所需的位元組數等函式,並且可以由使用者空間使用來定義在播放開始之前需要在環形緩衝區中寫入多少內容。

set_params

此例程設定為特定編解碼器選擇的配置。 引數中最重要的欄位是編解碼器型別; 在大多數情況下,解碼器將忽略其他欄位,而編碼器將嚴格遵守設定

get_params

此例程返回 DSP 使用的實際設定。 對設定的更改應仍然是例外。

get_timestamp

時間戳變成了一個多欄位結構。 它列出了傳輸的位元組數、處理的樣本數和渲染/抓取的樣本數。 所有這些值都可用於確定平均位元率,確定是否需要重新填充環形緩衝區或由於 DSP 上的解碼/編碼/ IO 引起的延遲。

請注意,編解碼器/配置檔案/模式列表是從 OpenMAX AL 規範派生的,而不是重新發明輪子。 修改包括: - 新增 FLAC 和 IEC 格式 - 合併編碼器/解碼器功能 - 配置檔案/模式列為位掩碼以使描述符更緊湊 - 新增解碼器的 set_params(OpenMAX AL 中缺少) - 新增 AMR/AMR-WB 編碼模式(OpenMAX AL 中缺少) - 新增 WMA 的格式資訊 - 新增所需的編碼選項(從 OpenMAX IL 派生) - 新增 rateControlSupported(OpenMAX AL 中缺少)

狀態機

壓縮音訊流狀態機如下所述

                                      +----------+
                                      |          |
                                      |   OPEN   |
                                      |          |
                                      +----------+
                                           |
                                           |
                                           | compr_set_params()
                                           |
                                           v
       compr_free()                  +----------+
+------------------------------------|          |
|                                    |   SETUP  |
|          +-------------------------|          |<-------------------------+
|          |       compr_write()     +----------+                          |
|          |                              ^                                |
|          |                              | compr_drain_notify()           |
|          |                              |        or                      |
|          |                              |     compr_stop()               |
|          |                              |                                |
|          |                         +----------+                          |
|          |                         |          |                          |
|          |                         |   DRAIN  |                          |
|          |                         |          |                          |
|          |                         +----------+                          |
|          |                              ^                                |
|          |                              |                                |
|          |                              | compr_drain()                  |
|          |                              |                                |
|          v                              |                                |
|    +----------+                    +----------+                          |
|    |          |    compr_start()   |          |        compr_stop()      |
|    | PREPARE  |------------------->|  RUNNING |--------------------------+
|    |          |                    |          |                          |
|    +----------+                    +----------+                          |
|          |                            |    ^                             |
|          |compr_free()                |    |                             |
|          |              compr_pause() |    | compr_resume()              |
|          |                            |    |                             |
|          v                            v    |                             |
|    +----------+                   +----------+                           |
|    |          |                   |          |         compr_stop()      |
+--->|   FREE   |                   |  PAUSE   |---------------------------+
     |          |                   |          |
     +----------+                   +----------+

無縫播放

在播放專輯時,解碼器能夠跳過編碼器延遲和填充,並直接從一個音軌內容移動到另一個音軌內容。 終端使用者可以將此感知為無縫播放,因為我們在從一個音軌切換到另一個音軌時沒有靜音。

此外,由於編碼,可能會出現低強度噪聲。 使用所有型別的壓縮資料很難達到完美的無縫效果,但在大多數音樂內容中效果很好。 解碼器需要知道編碼器延遲和編碼器填充。 因此,我們需要將其傳遞給 DSP。 此元資料是從 ID3/MP4 標頭中提取的,預設情況下不存在於位元流中,因此需要一個新的介面將此資訊傳遞給 DSP。 此外,DSP 和使用者空間需要從一個音軌切換到另一個音軌,並開始使用第二個音軌的資料。

主要的補充是

set_metadata

此例程設定編碼器延遲和編碼器填充。 解碼器可以使用此功能來去除靜音。 這需要在寫入音軌中的資料之前進行設定。

set_next_track

此例程告訴 DSP 在此之後傳送的元資料和寫入操作將對應於後續音軌

partial drain

當到達檔案結尾時呼叫此函式。 使用者空間可以通知 DSP 已到達 EOF,現在 DSP 可以開始跳過填充延遲。 此外,下一個寫入資料將屬於下一個音軌

無縫的順序流程將是: - 開啟 - 獲取 caps / codec caps - 設定 params - 設定第一個音軌的元資料 - 填充第一個音軌的資料 - 觸發啟動 - 使用者空間完成傳送所有內容, - 透過傳送 set_next_track 來指示下一個音軌資料 - 設定下一個音軌的元資料 - 然後呼叫 partial_drain 以重新整理 DSP 中的大部分緩衝區 - 填充下一個音軌的資料 - DSP 切換到第二個音軌

(注意:partial_drain 和下一個音軌的寫入順序也可以顛倒)

無縫播放 SM

對於無縫播放,我們將從執行狀態移動到 partial drain 並返回,同時設定 meta_data 併發出下一個音軌的訊號

                          +----------+
  compr_drain_notify()    |          |
+------------------------>|  RUNNING |
|                         |          |
|                         +----------+
|                              |
|                              |
|                              | compr_next_track()
|                              |
|                              V
|                         +----------+
|    compr_set_params()   |          |
|             +-----------|NEXT_TRACK|
|             |           |          |
|             |           +--+-------+
|             |              | |
|             +--------------+ |
|                              |
|                              | compr_partial_drain()
|                              |
|                              V
|                         +----------+
|                         |          |
+------------------------ | PARTIAL_ |
                          |  DRAIN   |
                          +----------+

不支援

  • 對 VoIP/電路交換呼叫的支援不是此 API 的目標。 對動態位元率更改的支援將需要在 DSP 和主機堆疊之間進行緊密耦合,從而限制功耗。

  • 不支援丟包隱藏。 這將需要一個額外的介面來讓解碼器在傳輸期間丟失幀時合成數據。 這可能會在將來新增。

  • 此 API 不處理音量控制/路由。 暴露壓縮資料介面的裝置將被視為常規 ALSA 裝置; 音量更改和路由資訊將透過常規 ALSA kcontrol 提供。

  • 嵌入式音訊效果。 無論輸入是 PCM 還是壓縮的,都應以相同的方式啟用此類效果。

  • 多聲道 IEC 編碼。 不清楚是否需要此功能。

  • 如上所述,不支援編碼/解碼加速。 可以將解碼器的輸出路由到捕獲流,甚至實現轉碼功能。 此路由將使用 ALSA kcontrol 啟用。

  • 音訊策略/資源管理。 此 API 不提供任何鉤子來查詢音訊 DSP 的利用率,也不提供任何搶佔機制。

  • 沒有欠載/超載的概念。 由於寫入的位元組本質上是壓縮的,並且寫入/讀取的資料不會直接轉換為時間的渲染輸出,因此這不處理欠載/超載,並且可能在使用者庫中處理

鳴謝

  • 感謝 Mark Brown 和 Liam Girdwood 討論對此 API 的需求

  • 感謝 Harsha Priya 在 intel_sst 壓縮 API 方面的工作

  • 感謝 Rakesh Ughreja 提供的寶貴反饋

  • 感謝 Sing Nallasellan、Sikkandar Madar 和 Prasanna Samaga 演示和量化音訊解除安裝在真實平臺上的優勢。