Android 音訊應用框架
frameworks\av\media\libmedia
--------AudioRecord.cpp
--------------|
|_ sp<IAudioRecord> record = audioFlinger->openRecord(input,
|_ mAudioRecord = record;
--------AudioSystem.cpp
--------AudioTrack.cpp
frameworks\av\services\audioflinger
--------------AudioFlinger.cpp
|_ AudioFlinger::openRecord(
|_ recordTrack = thread->createRecordTrack_l
|_ recordHandle = new RecordHandle(recordTrack);
|_
|_ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
|_ load_audio_interface
|_ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name,
|_ audio_hw_device_open(mod, dev);
|_
--------------AudioHwDevice.cpp
--------------Threads.cpp
|_ AudioFlinger::RecordThread::createRecordTrack_l(
|_ track = new RecordTrack(this, client, sampleRate,
|_
--------------Tracks.cpp
Audio Application Framework:音訊應用框架
|---AudioTrack:負責回放資料的輸出,屬 Android 應用框架 API 類
|---AudioRecord:負責錄音資料的採集,屬 Android 應用框架 API 類
|---AudioSystem: 負責音訊事務的綜合管理,屬 Android 應用框架 API 類
Audio Native Framework:音訊本地框架
|---AudioTrack:負責回放資料的輸出,屬 Android 本地框架 API 類
|---AudioRecord:負責錄音資料的採集,屬 Android 本地框架 API 類
|---AudioSystem: 負責音訊事務的綜合管理,屬 Android 本地框架 API 類
Audio Services:音訊服務
|----AudioPolicyService:音訊策略的制定者,負責音訊裝置切換的策略抉擇、音量調節策略等
|----AudioFlinger:音訊策略的執行者,負責輸入輸出流裝置的管理及音訊流資料的處理傳輸
Audio HAL:音訊硬體抽象層,負責與音訊硬體裝置的互動,由 AudioFlinger 直接呼叫
AudioTrack API 概述
播放聲音可以使用 MediaPlayer 和 AudioTrack,兩者都提供 Java API 給應用開發者使用。
兩者的差別在於:
MediaPlayer 可以播放多種格式的音源,如 mp3、flac、wma、ogg、wav 等,
而 AudioTrack 只能播放解碼後的 PCM 資料流。
瞭解下音訊重取樣:音訊重取樣是指這樣的一個過程——把一個取樣率的資料轉換為另一個取樣率的資料。
Android 原生系統上,音訊硬體裝置一般都工作在一個固定的取樣率上(如 48 KHz),因此所有音軌資料
都需要重取樣到這個固定的取樣率上,然後再輸出。
為什麼這麼做?系統中可能存在多個音軌同時播放,而每個音軌的取樣率可能是不一致的;
比如在播放音樂的過程中,來了一個提示音,這時需要把音樂和提示音混音並輸出到硬體裝置,
而音樂的取樣率和提示音的取樣率不一致,問題來了,如果硬體裝置工作的取樣率設定為音樂的取樣率的話,
那麼提示音就會失真;因此最簡單見效的解決方法是:硬體裝置工作的取樣率固定一個值,所有音軌在
AudioFlinger 都重取樣到這個取樣率上,混音後輸出到硬體裝置,保證所有音軌聽起來都不失真
Google 把多個子類抽取出來獨立成檔案,比如 Threads.cpp、Tracks.cpp、Effects.cpp,
而 AudioFlinger.cpp 只包含對外提供的服務介面了
|----AudioResampler.cpp:重取樣處理類,可進行取樣率轉換和聲道轉換;由錄製執行緒
AudioFlinger::RecordThread 直接使用
|----AudioMixer.cpp:混音處理類,包括重取樣、音量調節、聲道轉換等,其中的重取樣複用了 AudioResampler;
由回放執行緒 AudioFlinger::MixerThread 直接使用
|----Effects.cpp:音效處理類
|----Tracks.cpp:音訊流管理類,可控制音訊流的狀態,如 start、stop、pause
|-----Threads.cpp:回放執行緒和錄製執行緒類;回放執行緒從 FIFO 讀取回放資料並混音處理,然後寫資料到輸出流裝置;
錄製執行緒從輸入流裝置讀取錄音資料並重取樣處理,然後寫資料到 FIFO
|-----AudioFlinger.cpp:AudioFlinger 對外提供的服務介面
AudioFlinger 服務啟動
從 Android 7.0 開始,AudioFlinger 在系統啟動時由 audioserver 載入(之前版本由 mediaserver 載入),
詳見 frameworks/av/media/audioserver/main_audioserver.cpp:
main_audioserver.cpp 編譯生成的可執行檔案存放在 /system/bin/audioserver,系統啟動時由 init 程式執行,
詳見 frameworks/av/media/audioserver/audioserver.rc:
ThreadBase:PlaybackThread 和 RecordThread 的基類
RecordThread:錄製執行緒類,由 ThreadBase 派生
PlaybackThread:回放執行緒基類,同由 ThreadBase 派生
MixerThread:混音回放執行緒類,由 PlaybackThread 派生,負責處理標識為 AUDIO_OUTPUT_FLAG_PRIMARY、AUDIO_OUTPUT_FLAG_FAST、 AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音訊流,MixerThread 可以把多個音軌的資料混音後再輸出
DirectOutputThread:直輸回放執行緒類,由 PlaybackThread 派生,負責處理標識為 AUDIO_OUTPUT_FLAG_DIRECT 的音訊流, 這種音訊流資料不需要軟體混音,直接輸出到音訊裝置即可
DuplicatingThread:複製回放執行緒類,由 MixerThread 派生,負責複製音訊流資料到其他輸出裝置,使用場景如主音效卡裝置、
藍芽耳機裝置、USB 音效卡裝置同時輸出
OffloadThread:硬解回放執行緒類,由 DirectOutputThread 派生,負責處理標識為 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音訊流,
這種音訊流未經軟體解碼的(一般是 MP3、AAC 等格式的資料),需要輸出到硬體解碼器,由硬體解碼器解碼成 PCM 資料
從 Audio HAL 中,我們通常看到如下 4 種輸出流裝置,分別對應著不同的播放場景:
|----primary_out:主輸出流裝置,用於鈴聲類聲音輸出,對應著標識為 AUDIO_OUTPUT_FLAG_PRIMARY 的音訊流和一個 MixerThread 回放執行緒例項
|----low_latency:低延遲輸出流裝置,用於按鍵音、遊戲背景音等對時延要求高的聲音輸出,對應著標識為 AUDIO_OUTPUT_FLAG_FAST 的音訊流和一個 MixerThread 回放執行緒例項
|----deep_buffer:音樂音軌輸出流裝置,用於音樂等對時延要求不高的聲音輸出,對應著標識為 AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音訊流和一個 MixerThread 回放執行緒例項
|----compress_offload:硬解輸出流裝置,用於需要硬體解碼的資料輸出,對應著標識為 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音訊流和一個 OffloadThread 回放執行緒例項
可能有人產生這樣的疑問:既然 primary_out 裝置一直保持開啟,那麼能耗豈不是很大?這裡闡釋一個概念:輸出流裝置屬於邏輯裝置, 並不是硬體裝置。所以即使輸出流裝置一直保持開啟,只要硬體裝置不工作,那麼就不會影響能耗。那麼硬體裝置什麼時候才會開啟呢? 答案是 PlaybackThread 將音訊資料寫入到輸出流裝置時
系統啟動時,就已經開啟 primary_out、low_latency、deep_buffer 這三種輸出流裝置,並建立對應的 MixerThread 了;
而此時 DirectOutputThread 與 OffloadThread 不會被建立,直到標識為 AUDIO_OUTPUT_FLAG_DIRECT/AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
的音訊流需要輸出時,才開始建立 DirectOutputThread/OffloadThread 和開啟 direct_out/compress_offload 裝置。
NuPlayer DataSource/Parser 解析 mp3、flac 等檔案得到資料編碼資訊,並在構造 AudioTrack 例項時作為引數傳入,AudioFlinger 將基於這些編碼資訊開啟 compress_offload 裝置。到這裡,大家明白了嗎?每個 mp3/flac 檔案的編碼資訊可能是不一樣的,比如 a.mp3 檔案的編碼資訊是 mp3&44.1KHZ&16bit… ,
而 b.flac 檔案的編碼資訊是 flac&48KHz&24bit…; 播放 a.mp3 時,AudioFlinger 開啟一個配置為 mp3&44.1KHz&16bit… 的
compress_offload 裝置,接著播放 b.flac,就需要關閉之前的 compress_offload 裝置,重新開啟一個配置為 flac&48KHz&24bit… 的 compress_offload 裝置。所以系統不會提前開啟 compress_offload 裝置,只有等到播放 mp3、flac 時取到明確的資料編碼資訊, 才基於這些編碼資訊開啟 compress_offload 裝置。
編碼資訊包含很多條目,切換音源時,是否編碼資訊有一點點不一樣,都需要重新開啟 compress_offload 裝置呢?不能執行時更新資訊到 DSP 嗎?其實 stagefright 和 compress_offload 是支援執行期更新某些資訊的,也就是無縫切換,至於是哪些資訊,依賴於 DSP 演算法 實現;有興趣深入的可以參考 sendMetaDataToHal() 和 compress_set_gapless_metadata() 。
AudioFlinger 音訊流管理
從 AudioTrack、PlaybackThread、輸出流裝置三者的關係圖中,我們看到 AudioTrack 把音訊流資料送入到對應的 PlaybackThread 中,那麼應用程式想控制這些音訊流的話,比如開始播放 start()、停止播放 stop()、暫停播放 pause(),怎麼辦呢?注意應用程式與 AudioFlinger 並不在一個程式上。這就需要 AudioFlinger 提供音訊流管理功能,並提供一套通訊介面可以讓應用程式跨程式控制 AudioFlinger 中的音訊流狀態
AudioFlinger 音訊流管理由 AudioFlinger::PlaybackThread::Track 實現,Track 與 AudioTrack 是一對一的關係,一個 AudioTrack 建立後,那麼 AudioFlinger 會建立一個 Track 與之對應;PlaybackThread 與 AudioTrack/Track 是一對多的關係,一個 PlaybackThread 可以掛著多個 Track。
音訊流控制最常用的三個介面:
|----AudioFlinger::PlaybackThread::Track::start:開始播放:把該 Track 置 ACTIVE 狀態,然後新增到 mActiveTracks 向量中,
最後呼叫 AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情況有變
|---AudioFlinger::PlaybackThread::Track::stop:停止播放:把該 Track 置 STOPPED 狀態,最後呼叫
AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情況有變
|----AudioFlinger::PlaybackThread::Track::pause:暫停播放:把該 Track 置 PAUSING 狀態,最後呼叫
AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情況有變
AudioFlinger::PlaybackThread::threadLoop() 得悉情況有變後,呼叫 prepareTracks_l() 重新準備音訊流和混音器:ACTIVE 狀態的 Track 會新增到 mActiveTracks,此外的 Track 會從 mActiveTracks 上移除出來,然後重新準備 AudioMixer。
可見這三個音訊流控制介面是非常簡單的,主要是設定一下 Track 的狀態,然後發個事件通知 PlaybackThread 就行,複雜的處理都在 AudioFlinger::PlaybackThread::threadLoop() 中了。
AudioFlinger::TrackHandle:Track 物件只負責音訊流管理業務,對外並沒有提供跨程式的 Binder 呼叫介面,而應用程式又需要對音訊流進行控制,所以需要一個物件來代理 Track 的跨程式通訊,這個角色就是 TrackHandle,AudioTrack 通過它與 Track 互動
AudioTrack:Android 音訊系統對外提供的一個 API 類,負責音訊流資料輸出;每個音訊流對應著一個 AudioTrack 例項,不同輸出標識的 AudioTrack 會匹配到不同的 AudioFlinger::PlaybackThread;AudioTrack 與 AudioFlinger::PlaybackThread 之間通過 FIFO 來交換音 頻資料,AudioTrack 是 FIFO 生產者,AudioFlinger::PlaybackThread 是 FIFO 消費者
IAudioTrack:IAudioTrack 是鏈結 AudioTrack 與 AudioFlinger 的橋樑;它在 AudioTrack 端的物件是 BpAudioTrack,在 AudioFlinger 端的 物件是 BnAudioTrack,從圖中不難看出,AudioFlinger::TrackHandle 繼承自 BnAudioTrack,而 AudioFlinger::TrackHandle 恰恰是 AudioFlinger::PlaybackThread::Track 的代理物件,所以 AudioTrack 得到 IAudioTrack 例項後,就可以呼叫 IAudioTrack 的介面與 AudioFlinger::PlaybackThread::Track 互動
相關文章
- android音視訊指南-媒體應用架構概述Android應用架構
- Android端實現多人音視訊聊天應用(一)Android
- 火爆的音訊聊天應用Clubhouse音訊恐洩露音訊
- 最棒的開源 Android 應用:聊天、影象、音訊等等Android音訊
- Android端實現多人音視訊聊天應用(二):多人視訊通話Android
- Loopback for Mac(虛擬音訊應用)OOPMac音訊
- 虛擬音訊應用:Loopback Mac音訊OOPMac
- iOS 9音訊應用播放音訊之iOS 9音訊播放進度iOS音訊
- Android五大應用框架Android框架
- android音視訊指南-管理音訊焦點Android音訊
- Android音視訊之MediaPlayer音視訊播放Android
- iOS 9音訊應用播放音訊之ios9音訊基本功能iOS音訊
- iOS 9音訊應用播放音訊之第一個ios9音訊例項iOS音訊
- android音視訊指南-響應媒體按鈕Android
- iOS 9音訊應用播放音訊之控制播放速度iOS音訊
- Android音視訊之MediaRecorder音視訊錄製Android
- 如何基於 ZEGO SDK 實現 Android 一對一音視訊聊天應用GoAndroid
- android 音訊播放 SoundPoolAndroid音訊
- iOS 9音訊應用播放音訊之第一個ios9音訊例項2iOS音訊
- Android音視訊播放器框架看這些就夠了Android播放器框架
- Android 音視訊 - MediaCodec 編解碼音視訊Android
- 36款頂級的開源音訊/視訊應用程式音訊
- 嵌入式音訊應用開發介紹音訊
- 音視訊互動平臺應用分析薦
- Android 音視訊開發 視訊編碼,音訊編碼格式Android音訊
- Android音訊處理知識(一)MediaRecorder錄製音訊Android音訊
- Android音訊(三)AudioPolicyServiceAndroid音訊
- TAS5805MPWPR 攜式音訊應用—數字音訊技術音訊
- VoIP Push 在海外音視訊業務中的應用
- 華為音訊編輯服務(Audio Editor Kit),快速構建應用音訊編輯能力音訊
- android音視訊指南-處理音訊輸出的變化Android音訊
- android應用安全——元件通訊安全(Intent)Android元件Intent
- 書訊:《Android應用開發揭祕》Android
- Android之五大應用開發框架Android框架
- .Net開發的音訊分離桌面應用,可用於提取背景音樂音訊
- Android音訊視覺化操作Android音訊視覺化
- android音視訊指南-MediaPlayer概述Android
- android音視訊指南-MediaRecorder概述Android