WebRTC 系列之音频会话管理
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 的相关研发工作。
WebRTC 系列之音频会话管理的更多相关文章
- iOS音频学习笔记三:音频会话管理
使用Audio Session API ,可以指定App需要的音频行为,比如,当播放音频时,使得其他应用App静音或者混和在一起,也可以指定当App的音频被中断(例如被电话)时的行为,还 ...
- SAP接口编程 之 JCo3.0系列(04) : 会话管理
在SAP接口编程之 NCo3.0系列(06) : 会话管理 这篇文章中,对会话管理的相关知识点已经说得很详细了,请参考.现在用JCo3.0来实现. 1. JCoContext 如果SAP中多个函数需要 ...
- WebRTC系列(1)-手把手教你实现一个浏览器拍照室Demo
1.WebRTC开发背景 由于业务需求,需要在项目中实现实时音视频通话功能,之前基于浏览器开发的Web项目要进行音视频通话,需要安装flash插件才能实现或者使用C/S客户端进行通信.随着互联网技术的 ...
- Nodejs之MEAN栈开发(八)---- 用户认证与会话管理详解
用户认证与会话管理基本上是每个网站必备的一个功能.在Asp.net下做的比较多,大体的思路都是先根据用户提供的用户名和密码到数据库找到用户信息,然后校验,校验成功之后记住用户的姓名和相关信息,这个信息 ...
- Java中的会话管理——HttpServlet,Cookies,URL Rewriting(译)
参考谷歌翻译,关键字直接使用英文,原文地址:http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url ...
- 微信公众号开发C#系列-7、消息管理-接收事件推送
1.概述 在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息.其中,某些事件推送在发生后,是允许 ...
- Java 中的会话管理—— HttpServlet,Cookies,URL Rewriting(转)
索引 1.什么是 Session? 2.Java 中的会话管理—— Cookie 3.Java Servlet 中的 Session —— HttpSession 理解 JSESSIONID Cook ...
- 从零搭建一个IdentityServer——会话管理与登出
在上一篇文章中我们介绍了单页应用是如何使用IdentityServer完成身份验证的,并且在讲到静默登录以及会话监听的时候都提到会话(Session)这一概念,会话指的是用户与系统之间交互过程,反过来 ...
- 3种web会话管理的方式
http是无状态的,一次请求结束,连接断开,下次服务器再收到请求,它就不知道这个请求是哪个用户发过来的.当然它知道是哪个客户端地址发过来的,但是对于我们的应用来说,我们是靠用户来管理,而不是靠客户端. ...
随机推荐
- python ---线程,进程,协程
本章内容 线程 进程 协程 线程是最小的调度单位 进程是最小的管理单元 线程 多线程的特点: 线程的并发是利用cpu上下文切换 多线程的执行的顺序是无序的 多线程共享全局变量 线程是继承在进程里的,没 ...
- Kibana,Logstash 和 Cerebro 的安装运行
公号:码农充电站pro 主页:https://codeshellme.github.io 1,安装 Kibana Kibana 用于数据可视化,我们可以进入到 Kibana 下载页面下载 Kibana ...
- 从单页应用(SPA)到服务器渲染(SSR)
从单页应用(SPA)到服务器渲染(SSR) 情景回顾 在学习Vue开发一个电商网站的管理后台时,使用到了一个组件 vue-quill-editor 主要是一个快捷的一个富文本编辑器 在使用这个组件的组 ...
- 模块化之CommonJS
一.CommonJS特点 经过前面讨论,已经知道无模块化时项目中存在的问题.CommonJS的特点就是解决这些问题即: 1.每个文件都是一个单独的模块,有自己的作用域,声明的变量不是全局变量( ...
- HanLP 下载和配置
方式一.Maven 为了方便用户,特提供内置了数据包的Portable版,只需在pom.xml加入: <dependency> <groupId>com.hankcs</ ...
- <<Hive编程指南>>读书笔记
1. 设置hive以本地模式运行(即使当前用户是在分布式模式或伪分布式模式下执行也使用这种模式) set hive.exec.model.local.auto=true; 若想默认使用这个配置,可以将 ...
- vs中python包安装教程
vs安装python很简单,只需要在vs安装包中选择python就可以了,这里使用的python3.7: 如果有了解,都知道安装python包的指令:"pip install xxx&quo ...
- hdu5726 GCD(gcd +二分+rmq)
Problem Description Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). ...
- Codeforces Round #633 div2 A~C
A. Filling Diamonds 题意:给你n个菱形方块,问能构成图示形状的有多少种 题解:自己画几个不难发现答案是n 代码: 1 #include <iostream> 2 #in ...
- 51Nod - 1632
B国拥有n个城市,其交通系统呈树状结构,即任意两个城市存在且仅存在一条交通线将其连接.A国是B国的敌国企图秘密发射导弹打击B国的交通线,现假设每条交通线都有50%的概率被炸毁,B国希望知道在被炸毁之后 ...