Android中的PCM設備


Android上的應用一般都是通過AudioTrack類來播放音頻,通過AudioRecord類來錄制音頻。AudioTrack類和AudioRecord類是Android Frameworks封裝提供給應用使用的音頻接口類。這些類經過層層的Binder、JNI等調用后會調用Audio HAL層提供的相關接口。這些接口實現了對音頻設備、通路等一系列操作。就這樣最終完成Android App和硬件的交互,實現聲音的播放或者錄制。
我們知道,在Linux系統中,所有的設備最終都是抽象成一個或者多個用戶空間可以訪問的設備文件,用戶空間的進程通過這些設備文件的讀寫來達到控制硬件的目的。而這些設備文件都是由內核空間中的驅動程序創建、實現的。手機上的音頻設備、接口比較多,對應的設備文件自然也比較多。對於播放聲音或者錄制聲音來說,Audio HAL層是通過對PCM設備文件的讀寫來實現的。

查看PCM設備列表

如果手機的音頻系統正常工作,我們可以通過adb去查看系統中的所有音頻設備文件。如下圖所示:

那些以pcm打頭的設備就是提供播放或錄音的設備即本文要探討的PCM設備,其他的設備提供效果、合成等功能。
音頻設備的命名規則為 [device type]C[card index]D[device index][capture/playback],即名字中含有4部分的信息:

  1. device type
    設備類型,通常只有compr/hw/pcm這3種。從上圖可以看到聲卡會管理很多設備,PCM設備只是其中的一種設備。
  2. card index
    聲卡的id,代表第幾塊聲卡。通常都是0,代表第一塊聲卡。手機上通常都只有一塊聲卡。
  3. device index
    設備的id,代表這個設備是聲卡上的第幾個設備。設備的ID只和驅動中配置的DAI link的次序有關。如果驅動沒有改變,那么這些ID就是固定的。
  4. capture/playback
    只有PCM設備才有這部分,只有c和p兩種。c代表capture,說明這是一個提供錄音的設備,p代表palyback,說明這是一個提供播放的設備。
    系統會在/proc/asound/pcm文件中列出所有的音頻設備的信息,如果是肉眼查看,/proc/asound/pcm中的信息會更直觀一些:

PCM設備文件的訪問

查看PCM設備文件的屬性,可看到它們都是字符型設備:

對於普通的字符型設備,我們都是通過系統調用open/read/write/close來訪問,有些設備支持隨機訪問,我們還可以使用lseek調用。PCM設備文件也是類似,不一樣的是,我們可以使用open/close來打開/關閉設備,讀取/寫入文件卻不是通過read/write,而都是通過ioctl來操作的。
在Android Audio HAL層中,是通過TinyAlsa來訪問PCM設備文件的。TinyAlsa封裝了一系列接口用於PCM設備的訪問,這些接口被Audio Hal調用以后,最終又會被Frameworks調用。接口包括:

  1. struct pcm *pcm_open(unsigned int card, unsigned int device, unsinged int flags, struct pcm_config *config)
  2. int pcm_close(struct pcm* pcm)
  3. int pcm_write(struct pcm pcm, const void data, unsigned int count)
  4. int pcm_read(struct pcm* pcm, void* data, unsigned int count)

還有很多其他接口,但我們不需要關心那些細節,除非想要再造一遍輪子。
從pcm_open這個接口可以看到,它通過幾個參數獲得了一個句柄,之后所有的操作都通過這個句柄來完成。這些參數里面,card代表第幾塊聲卡,device就是上面提到的device index,它跟驅動中配置的DAI link的次序有關,flags參數中會指明這個設備是capture類型還是playback類型。通過這3個參數,就可以找到對應的PCM設備文件,例如 /dev/snd/pcmC0D5p,然后就可以去獲取操作它的句柄,然后做更多的操作。

PCM設備的管理 —— PCM ID

如果我們查看不同型號的手機上的音頻設備列表,可以看到它們幾乎都是不一樣的,有的手機設備文件多,有的手機設備文件少。實際上不只是設備文件個數的差別,還有可能出現在A型號手機上speaker對應的PCM設備文件是/dev/snd/pcmC0D4p,而在B型號手機上speaker對應的PCM設備文件卻是/dev/snd/pcmC0D7P。因此,Audio HAL中需要考慮到這種場景差異的影響。
我們已經知道,使用TinyAlsa接口訪問PCM設備的時候,需要知道設備的id,這個id我們稱為PCM ID,那么如何知道某一個設備例如speaker的PCM ID呢?另外,在不接耳機的時候,我們希望聲音從speaker出來,這時候需要知道的是speaker的PCM ID,如果接了耳機,我們又希望聲音從耳機出來,這時候需要知道的是耳機的PCM ID。
針對這些問題、需求,Audio HAL的設計是根據使用場景(USECASE)來決定要使用哪些設備的,即為各個USECASE分別定義好要使用的PCM設備的ID。Frameworks通過USECASE來指定PCM設備,而Audio HAL通過USECASE來找到PCM設備文件路徑。
如下圖所示,Audio HAL通過pcm_device_table這張表完成USECASE到PCM ID的映射。因此,PCM設備的管理實際上就是管理這張映射表。

USECASE與PCM ID的映射關系可以通過配置文件來修改,這樣的好處就是所有產品可以使用同一套代碼,差異的地方可以通過控制配置文件的內容就可以簡單的區分開來,Audio HAL會解析這個配置文件,然后更新映射表內容。使用adb shell cat /system/etc/audio_platform_info.xml可以看到如下配置:

如上圖,就會把USECASE_AUDIO_PLAYBACK_LOW_LATENCY這個使用場景的PCM ID更新為12。其他應用場景的PCM ID更新也是類似。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM