WebRTC(Web Real-Time Communication)是一個支持網頁瀏覽器進行實時語音對話或視頻對話的 API。W3C 和 IETF 在2021年1月26日共同宣布 WebRTC 1.0 定稿,促使 WebRTC 從事實上的互聯網通信標准成為了官方標准,其在不同場景的應用將得到更為廣泛的普及。
WebRTC 提供了視頻會議的核心技術,包括音視頻的采集、編解碼、網絡傳輸、顯示等功能,並且還支持跨平台:Windows,Mac,iOS,Android。本文主要介紹 WebRTC 其 iOS 平台的音頻會話 AVAudioSession。關於 WebRTC 往期相關技術分享,在文末有集合,也歡迎持續關注~
概念介紹
iOS 音頻會話 AVAudioSession 是每一個進行 iOS 音頻開發的開發者必須了解的基本概念。音頻會話在操作系統 iOS、tvOS、watchOS 中是一項托管服務,系統通過音頻會話在應用程序內、應用程序間和設備間管理音頻行為。
我們可以使用音頻會話來與系統交流,計划如何在應用程序中使用音頻。此時,音頻會話充當應用程序與操作系統之間的中介,進而充當基礎音頻硬件之間的中介。我們可以使用它向操作系統傳達應用程序音頻的性質,而無需詳細說明特定行為或與音頻硬件的必要交互。將這些細節的管理委派給音頻會話,可以確保對用戶的音頻體驗進行最佳管理。
下圖出自《Audio Session Programming Guide》,從圖中可以看到 AVAudioSession 就是用來管理多個 APP 對音頻硬件設備的資源使用。
音頻會話的能力
iOS 的音頻會話能力主要分為以下幾種情況:
- 配置音頻會話
- 激活音頻會話
- 響應中斷
- 響應路由更改
- 配置設備硬件
- 保護用戶隱私
具體的詳細說明可以參考官網:《Audio Session Programming Guide》,這里就不再全盤細說,本文將主要分析配置音頻會話、配置設備硬件兩方面的技術細節以及分享實際開發過程中踩過的坑。
配置音頻會話
AVAudioSession 的 Category、CategoryOption、Mode 配合使用,不同的應用類型或者使用場景需要搭配不同的組合。
Category 主要有以下7種類型,其主要描述以及特點在表中詳細介紹:
CategoryOption 主要有以下7種類型:
下圖詳細列出了 Category、CategoryOption、Mode 配合使用情況:
分類表達音頻角色
表達音頻行為的主要機制是使用音頻會話類別。通過設置類別,我們可以指示應用程序是使用音頻輸入還是音頻輸出,例如是否需要麥克風的采集、是否需要揚聲器的播放等等。下文主要介紹音頻會話類別 Category 在不同場景的應用,針對不同的 Category 的區別,可以對應上文表一詳細查看。
游戲應用的場景
大多數游戲都需要用戶交互發生在游戲中。用戶調出另一個應用程序或鎖定屏幕時,他們不希望該應用程序繼續播放。設計游戲時,可以使用 AVAudioSessionCategoryAmbient 或 AVAudioSessionCategorySoloAmbient 類別。
用戶控制的播放和錄制應用程序的場景
錄制應用程序和播放應用程序具有相似的准則。這些類型的應用程序的使用 AVAudioSessionCategoryRecord,AVAudioSessionCategoryPlayAndRecord 或 AVAudioSessionCategoryPlayback 類別。
VoIP 和聊天應用程序的場景
VoIP 和聊天應用程序要求輸入和輸出路由均可用。這些類型的應用程序使用 AVAudioSessionCategoryPlayAndRecord 類別,並且不會與其他應用程序混合使用。
計量應用的場景
計量應用程序需要應用到輸入和輸出路徑的系統提供的信號處理量最少。設置 AVAudioSessionCategoryPlayAndRecord 類別和測量模式以最小化信號處理。此外,此類型的應用程序不能與其他應用程序混合使用。
播放音頻類似瀏覽器的應用程序場景
社交媒體或其他類似瀏覽器的應用程序經常播放短視頻。他們使用 AVAudioSessionCategoryPlayback 類別,並且不服從鈴聲開關。這些應用程序也不會與其他應用程序混合使用。
導航和健身應用程序的場景
導航和鍛煉應用程序使用 AVAudioSessionCategoryPlayback 或 AVAudioSessionCategoryPlayAndRecord 類別。這些應用的音頻通常包含簡短的語音提示。播放時,這些提示會中斷口語音頻(例如播客或有聲書),並與其他音頻(例如從“音樂”應用中播放)混音。
合作音樂應用的場景
合作音樂應用程序旨在播放其他應用程序時播放。這些類型的應用程序使用 AVAudioSessionCategoryPlayback 或 AVAudioSessionCategoryPlayAndRecord 類別,並與其他應用程序混合使用。
網易雲信使用情況
表中為網易雲信在不同的使用場景下,Category、Options、Mode 的配置情況:
針對上表的內容,我們也整理了一些常見的問題:
-
問題一:相信了解這塊的小伙伴們肯定會有疑問,為什么 NERTC 實時音視頻 SDK 默認不帶 DefaultToSpeaker 選項嗎?
-
答:其實原來我們使用聽筒和揚聲器切換都是使用 AVAudioSessionCategoryOptions 帶AVAudioSessionCategoryOptionDefaultToSpeaker 和 不帶AVAudioSessionCategoryOptionDefaultToSpeaker 操作的;當 APP 為揚聲器時,AVAudioSessionCategoryOptions 帶 AVAudioSessionCategoryOptionDefaultToSpeaker;而 callkit 本身切換聽筒和揚聲器應該使用的輸出路由的變更 overrideOutputAudioPort: 操作的,因此切到聽筒 AVAudioSessionPortOverrideNone 時,由於 category 和 categoryOptions 都沒有變化,因此無法切換。最后 SDK 內部 AVAudioSessionCategoryOptions 將不再攜帶 AVAudioSessionCategoryOptionDefaultToSpeaker;同時揚聲器和聽筒切換都改為 overrideOutputAudioPort: 操作。
-
問題二:AVAudioSessionPortOverrideSpeaker 和AVAudioSessionCategoryOptionDefaultToSpeaker 之間的區別是什么呢?
-
答:區別在於,AVAudioSessionPortOverride 通過調用 overrideOutputAudioPort:,設置的時間要比使用 category 選項 AVAudioSessionCategoryOptionDefaultToSpeaker 更短暫。調用overrideOutputAudioPort:和設置 AVAudioSessionPortOverride 到 AVAudioSessionPortOverrideSpeaker 是暫時壓倒一切的輸出將其路由到揚聲器的方式。遵循后進制勝規則,任何路線更改或中斷都將導致音頻被路由回到其正常路線。考慮使用overrideOutputAudioPort: 可能用於實現免提電話按鈕的方式,在該按鈕上您希望能夠在揚聲器(AVAudioSessionPortOverrideSpeaker)和正常輸出路線(AVAudioSessionPortOverrideNone)之間切換。AVAudioSessionCategoryOptionDefaultToSpeaker 修改 AVAudioSessionCategoryPlayAndRecord 類別的路由行為,以便在不使用其他附件(例如耳機)的情況下,音頻將始終路由到揚聲器,而不是接收器。使用時AVAudioSessionCategoryOptionDefaultToSpeaker,將尊重用戶的手勢。例如,插入耳機將導致路由更改為耳機麥克風/耳機,拔出耳機將導致路由更改為內置麥克風/揚聲器(與內置麥克風/接收器相反)被設置。
-
問題三:為什么 NERTC 實時音視頻 SDK 會有2種模式?
-
答:因為 VoIP 情況下會使用AVAudioSessionModeVoiceChat模式,在 RemoteIO 情況下,會使用AVAudioSessionModeDerfault模式。
-
問題四:在AVAudioSessionCategoryPlayAndRecord類別下,Mode 有哪些隱藏含義?
-
答:設置AVAudioSessionModeVoiceChat模式將啟用AVAudioSession類別選項AVAudioSessionCategoryOptionAllowBluetooth,從而進一步修改類別的行為,AVAudioSessionCategoryPlayAndRecord以允許將配對的藍牙免提配置文件(HFP)設備用於輸入和輸出。設置AVAudioSessionModeVideoChat模式將AVAudioSessionCategoryPlayAndRecord通過設置AVAudioSessionCategoryOptionAllowBluetooth選項和AVAudioSessionCategoryOptionDefaultToSpeaker選項,進一步修改類別的行為。
網易雲音樂使用情況
正常音樂軟件使用 AVAudioSessionCategoryPlayback 類別,在雲音樂新上線的“一起聽”功能模塊會使用實時音視頻,因此使用 AVAudioSessionCategoryPlayAndRecord 類別。因為音樂軟件對音質要求比較高,所以在藍牙情況下,會使用 A2DP 模式,使用 AVAudioSessionCategoryOptionAllowBluetoothA2DP。
配置設備硬件
使用音頻會話屬性,可以在運行時針對設備硬件優化應用程序的音頻行為。這樣做可以使我們的代碼適應正在運行設備的特性,以及用戶在應用程序運行時所做的更改(例如插入耳機或將設備對接)。
我們在配置設備硬件 ,可以通過 AVAudioSession 實現相應的屬性配置:
- 為采樣率和 I / O 緩沖區持續時間指定首選的硬件設置。
- 查詢許多硬件特性,例如輸入和輸出延遲,輸入和輸出通道數,硬件采樣率,硬件音量設置以及音頻輸入的可用性。
想要配置設備硬件,首先需要了解清楚硬件的詳細情況,具體可以看下面的2張圖,自行測試和查閱文檔得出。
iPhone 硬件詳情:
iPad 硬件詳情:
問題排查
音頻可用性問題
音頻可用性問題通常指音頻的采集和播放是否正常工作,那么會有哪些因素會影響音頻的可用性呢?
- 設備權限:無麥克風權限、沒有配置音頻后台權限。
- 被其他聲音搶占,如微信通話、打電話中斷、siri 中斷等。
- 用戶行為:接口調用 mute,修改 AVAudioSession 的 Category 等。
- 機器故障:硬件啟動失敗。
為了應對音頻問題的排查,我們新增了音頻事件上報和音頻回路檢測功能。
iOS 音頻事件上報類型
這里我們羅列幾種常見的 iOS 音頻事件上報的具體類型:
- 音頻輸入設備變更事件:上報設備名,如【 麥克風 (BuiltInMic)、普通耳機 (HeadsetMic)、藍牙耳機 (BluetoothHFP)(一般指HFP)】
- 示例: {"InputDeviceChange":"BuiltInMic"}
- 音頻輸出設備變更事件:上報設備名,如【 揚聲器 (BuiltInSpeaker)、聽筒 (BuiltInReceiver)、普通耳機 (Headphones)、藍牙耳機 (BluetoothHFP)、藍牙耳機 (BluetoothLE)、藍牙耳機 (BluetoothA2DP)】
- 示例:{"OutputDeviceChange":"BuiltInSpeaker"}
- 音頻采集采樣率變更事件:上報當前采集采樣率
- 示例:{"RecordSampleRateChange":"48000"}
- 音頻播放采樣率變更事件:上報當前播放采樣率
- 示例:{"PlayoutSampleRateChange":"48000"}
- 音頻設備異常狀態變更事件:上報當前異常狀態
- 示例:{"InitRecordingErr\StartRecordingErr\StopRecordingErr...":"-108"}
- 音頻系統音量變更事件:上報當前系統音量
- 示例:{"SystemVolumeChange":"70"}
- 音頻播放故障檢測變更事件:上報當前播放故障次數
- 示例:{"PlayoutGlitch":"3"}
- 音頻會話相關變更事件有以下8種情況:
- 音頻打斷開始事件 audioInterruptionBegin 0
- 音頻打斷結束事件 audioInterruptionEnd 1
- 音頻媒體服務丟失事件 audioMediaServicesWereLost 2
- 音頻媒體服務重置事件 audioMediaServicesWereReset 3
- 沉默輔助音頻提示通知開始事件 audioSilenceSecondaryAudioHintBegin 4
- 沉默輔助音頻提示通知結束事件 audioSilenceSecondaryAudioHintEnd 5
- 示例:{"audioInterruptionBegin\audioInterruptionEnd...":"0"}
- AVAudioSession 相關的 Category 6
- 示例: {"CategoryChange":"AVAudioSessionCategoryPlayAndRecord"}
- AVAudioSession 相關的 CategoryOption 7
- 示例:{"CategoryOptionChange":"37"}
- AVAudioSession 相關的 Mode 8
- 示例:{"ModeChange":"AVAudioSessionModeVoiceChat"}
音頻回路檢測
我們在使用過程中需要實時觀察音頻的回路工作狀態,我們重點分析以下兩種情況:
- 當我們需要准確了解音頻回路的實際工作狀態,那么我們可以通過采集音頻和播放音頻對應的采樣率以及單位時間內的音頻采樣 Samples 偏差,同時也能了解到它向NetEQ(即:音頻 Buffer) 索要音頻播放數據的節奏。
- 當播放線程出現卡頓的時候,我們需要實時獲取音頻播放線程狀態以及分析解碼、混音等各個階段的耗時,排查其他環節對於播放線程的影響。
未來展望
產品以及系統也在不斷迭代升級,例如:
- 麥克風的位置選擇(Built-in 不可控,因為要完美處理回聲問題)和麥克風的極性模式設置(極性模式定義了其對聲音相對於聲源方向的靈敏度)。
- 從 iOS 14 和 iPadOS 14 開始,我們現在可以使用支持的設備上的內置麥克風來捕獲立體聲音頻,從而獲得非常有沉浸式的錄音體驗。
我們期望未來可以結合這些優勢能力,在音頻會話技術上深度挖掘,以提供更好的音頻服務。
總結
本文介紹了基於 WebRTC 實現 iOS 音頻會話的實現以及管理。一個完整的系統,需要有預警各種異常、處理各種異常情況的能力。由於移動端設備的復雜性,移動端音頻預警,自行恢復機制是一個比較核心的技術。
熟練掌握 AVAudioSession 的 Category、CategoryOption、Mode 的各個含義和了解 iPhone、iPad的硬件構造,對於理解 iOS 音頻至關重要,上文如有不正確之處,歡迎指出,也歡迎交流。
系列文章
作者介紹
陶金亮,網易雲信資深客戶端音視頻工程師,一直從事客戶端音視頻相關開發工作,期間負責網易雲信的 G1 和 G2 的相關研發工作。