IOS音视频(四十三)AVFoundation 之 Audio Session
1.音频会话概述
音频是iOS、tvOS和watchOS中的托管服务。系统通过使用音频会话来管理应用程序、应用程序间和设备级别上的音频行为。
你使用一个音频会话来与系统沟通,告诉系统你打算如何在你的应用程序中使用音频。这个音频会话充当了你的应用程序和操作系统之间的媒介,反过来,也就是底层音频硬件之间的媒介。你可以使用它来与操作系统交流应用程序音频的性质,而无需详细说明具体的行为或与音频硬件的必要交互。将这些细节的管理委托给音频会话可以确保对用户音频体验的最佳管理。
你与你的应用程序的音频会话使用AVAudioSession的一个实例:
- 配置音频会话类别和模式,以向系统传达您打算如何在应用程序中使用音频
- 激活你的应用程序的音频会话,把你的类别和模式配置到行动
- 订阅和响应重要的音频会话通知,如音频中断和路由更改
- 执行高级音频设备配置,如设置采样速率、I/O缓冲区持续时间和通道数量
-
音频会话管理音频行为
音频会话是应用程序和操作系统之间的中介,用于配置应用程序的音频行为。在启动时,你的应用程序会自动提供一个单例音频会话。您可以配置它来提供所需的行为,并激活它来将该行为转化为操作。 -
类别表示音频角色
表达音频行为的主要机制是音频会话类别。通过设置类别,您可以指示应用程序是否使用输入或输出路径,是否希望音乐与音频一起继续播放,等等。你指定的行为应该满足用户的期望,正如在iOS人机界面指南音频中描述的那样。
AVFoundation定义了许多音频会话类别,以及一组覆盖和修改开关,允许您根据应用程序的个性或角色自定义音频行为。各种类别支持回放、录制和带有录制的回放。当系统知道您的应用程序的音频角色时,它将为您提供对硬件资源的适当访问。该系统还确保设备上的其他音频以适合您的应用程序的方式工作,并符合用户的期望。
某些类别还可以通过指定用于专门化给定类别的行为的模式来进一步定制。例如,当应用程序使用视频录制模式时,系统可能会选择不同于使用默认模式时的内置麦克风。该系统还可能采用为视频录制用例进行调优的麦克风信号处理。
-
通知支持中断处理
音频中断是应用程序的音频会话的停用,它会立即停止音频。当来自应用程序的竞争音频会话被激活,并且该会话未被系统分类以与您的会话混合时,就会出现中断。您的应用程序应该通过保存状态、更新用户界面等方式来响应中断。要在音频中断开始和结束时得到通知,请注册以观察AVAudioSessionInterruptionNotification类型的通知。 -
通知支持音频路由更改处理
当用户通过连接或断开设备、插入或断开耳机来启动音频路由更改时,他们有特定的期望。iOS人机界面指南描述了这些期望,并提供了如何满足这些期望的指南。通过注册来观察AVAudioSessionRouteChangeNotification类型的通知来处理路由更改。 -
音频会话控制设备配置
应用程序不能直接控制设备硬件,但音频会话为您提供了请求首选硬件设备设置的接口。这个接口使您能够执行高级音频设备配置,如设置采样速率、I/O缓冲区持续时间和音频通道的数量。 -
音频会话保护用户隐私
单独或与视频一起录制音频的应用程序,在允许录制之前,需要明确的用户许可。在用户授予你的应用录制权限之前,应用只能录制静音。AVAudioSession提供了请求此权限并确定用户隐私设置的接口。
2. 配置音频会话
音频会话类别是一个键,用于识别应用程序的一组音频行为。通过设置一个类别,您可以向系统表明您的音频意图——例如,当铃声/静音开关翻转时,音频是否应该继续。几个音频会话类别,以及一组覆盖和修改开关,让您自定义您的应用程序的音频行为。
如表B-1所示,每个音频会话类别都指定了一组特定的响应,以响应下列每个行为:
- 中断非混合应用程序音频:如果是,当你的应用程序激活其音频会话时,非混合应用程序被中断。
- 静音开关静音:如果是,当用户激活静音开关时,您的音频将被静音。(在iPhone上,这个开关叫做铃声/静音开关。)
- 支持音频输入:如果支持,则允许app音频输入(录制)。
- 支持音频输出:如果是,应用程序音频输出(播放)是允许的。
大多数应用程序只需要在启动时设置类别一次,但你可以根据需要随时更改类别。你可以改变它,而音频会话是积极的;然而,在更改类别或其他会话属性之前,通常更可取的做法是禁用音频会话。在会话被停用时进行这些更改可以防止音频系统进行不必要的重新配置。
2.1 音频会话默认行为
所有的iOS、tvOS和watchOS应用程序都有一个预设的默认音频会话,如下所示:
- 支持音频播放,但不允许录制音频。
- 在iOS系统中,将铃声/静音开关设置为静音模式,应用程序播放的任何音频都会被静音。
- 在iOS系统中,当设备被锁定时,应用程序的音频会被静音。
- 当您的应用程序播放音频时,任何其他背景音频(如音乐应用程序播放的音频)都将被静音。
默认的音频会话具有有用的行为,但在大多数情况下,您应该自定义它以更好地满足您的应用程序的需要。要更改行为,需要配置应用程序的音频会话。
2.2 配置音频会话
配置音频会话的主要方法是设置其类别。音频会话类别定义了一组音频行为。与每个类别相关的精确行为并不在应用程序的控制之下,而是由操作系统设置的。苹果可能会在未来的操作系统版本中完善类别行为,所以你最好的策略是选择最准确地描述你想要的音频行为意图的类别。音频会话类别和模式总结了每个类别的行为细节。
虽然类别设置了应用程序的基本音频行为,但您可以通过设置类别的模式进一步专门化这些行为。例如,IP语音(VoIP)应用程序将使用AVAudioSessionCategoryPlayAndRecord。您可以通过将音频会话模式设置为AVAudioSessionModeVoiceChat来专门化VoIP应用程序的此类行为。这种模式确保通过系统提供的信号处理来优化语音信号。
AVAudioSessionCategoryPlayAndRecord模式如下:
static let playAndRecord: AVAudioSession.Category
- 您的音频继续与静音开关设置为静音和与屏幕锁定。(这个开关在iPhone上被称为铃声/静音开关。)为了在你的应用程序转换到背景时(例如,当屏幕锁定时)继续播放音频,在你的信息属性列表文件的UIBackgroundModes键中添加音频值。
- 这个类别适用于同时录制和回放,也适用于录制和回放但不是同时播放的应用程序。
- 默认情况下,使用这个类别意味着你的应用程序的音频是不可混合的——激活你的会话将中断任何其他不可混合的音频会话。要允许混合此类别,请使用mixWithOthers选项。
- 用户必须为音频录制授予权限(参见录制需要用户权限)。
这个类别支持Airplay的镜像版本。然而,如果AVAudioSessionModeVoiceChat模式用于此类别,则AirPlay镜像将被禁用。- 某些类别支持通过在会话上设置一个或多个类别选项来覆盖其默认行为(参见AVAudioSessionCategoryOptions)。例如,当会话被激活时,与AVAudioSessionCategoryPlayback类别相关联的默认行为会中断其他系统音频。在大多数情况下,回放应用程序需要这种行为。但是,如果您希望您的音频与其他系统音频混合,您可以通过在会话中设置AVAudioSessionCategoryOptionMixWithOthers选项来覆盖此行为。
- 音频会话模式有:
注意:当铃声/静音开关设置为静音并锁定屏幕时,为了让你的应用程序继续播放音频,请确保UIBackgroundModes音频键已添加到你的应用程序的信息中。plist文件。这个要求是除了你使用正确的类别。
- 模式及相关类别:
要设置音频会话类别(以及可选的模式和选项),请调用setCategory:mode:options:error: method,使用AVFoundation框架设置音频会话类别代码如下:
// Access the shared, singleton audio session instance
let session = AVAudioSession.sharedInstance()
do {
// Configure the audio session for movie playback
try session.setCategory(AVAudioSessionCategoryPlayback,
mode: AVAudioSessionModeMoviePlayback,
options: [])
} catch let error as NSError {
print("Failed to set the audio session category and mode: \(error.localizedDescription)")
}
2.3 使用多路由类别扩展选项
多路由类别的工作方式与其他类别略有不同。所有其他类别的设备都遵循“最后入网”规则,即最后插入输入或输出路由的设备是占主导地位的设备。但是,多路由类允许应用程序使用所有连接的输出端口,而不是仅使用最后一个端口。例如,如果您正在通过HDMI输出路径听音频,并插入一组耳机,您的应用程序将继续通过HDMI输出路径播放音频,同时也通过耳机播放音频。
使用multiroute类别,您的应用程序还可以将不同的音频流发送到不同的输出路径。例如,您的应用程序可以将一个音频流发送到左侧耳机,另一个发送到右侧耳机,第三个发送到HDMI路由。图1-1显示了将多个音频流发送到不同音频路由的示例。
根据设备和任何连接的附件,以下是有效的输出路径组合:
- USB和耳机
- HDMI和耳机
- 列队争球和耳机
多路由类别支持使用单个输入端口。
重要提示:只有在没有其他合适的输出端口(USB、HDMI、LineOut)连接时才能使用内置扬声器。
2.4 为AirPlay选择类别和模式
只有特定的类别和模式支持AirPlay。以下类别同时支持镜像和非镜像版本的AirPlay:
AVAudioSessionCategorySoloAmbient
AVAudioSessionCategoryAmbient
AVAudioSessionCategoryPlayback
AVAudioSessionCategoryPlayAndRecord类别和以下模式只支持镜像版本的AirPlay:
AVAudioSessionModeDefault
AVAudioSessionModeVideoChat
AVAudioSessionModeGameChat
注意:从ios10开始,当你使用AVAudioSessionCategoryPlayAndRecord目录时,你可以通过AVAudioSessionCategoryOptionAllowAirPlay选项激活你的会话来启用非镜像AirPlay输出。
AVAudioSessionCategoryOptionAllowAirPlay = 0x40
- 只有在音频会话类别为AVAudioSessionCategoryPlayAndRecord时,才能显式设置此选项。对于大多数其他音频会话类别,系统将隐式地设置此选项。使用AVAudioSessionCategoryMultiRoute或AVAudioSessionCategoryRecord类别的音频会话隐式地清除此选项。
- 如果您清除此选项,AirPlay设备将不会显示为可用的音频输出路由。如果设置此选项,这些设备将显示为可用的输出路由。
2.5 开启后台音频
iOS和tvOS应用程序要求您为某些后台操作启用某些功能。回放应用程序需要的一个常见功能是播放背景音频。启用这个功能后,当用户切换到其他应用程序或锁定iOS设备时,应用程序的音频可以继续。在iOS中支持高级回放功能,如AirPlay流媒体播放和图片中的图片回放,也需要此功能。
配置这些功能的最简单方法是使用Xcode。在Xcode中选择应用程序的目标并选择capability选项卡。在capability选项卡下,将背景模式设置为ON,并从可用模式列表中选择“Audio, AirPlay, and Picture in Picture”选项。
启用此后台模式,并将音频会话配置为适当的类别,您的应用程序就可以开始播放后台音频了
注意:要允许视频演示的音频部分在后台播放,请参阅媒体播放编程指南中的播放背景音频。
3. 激活音频会话
您已经通过设置音频会话的类别、选项和模式来配置它。要使配置生效,现在需要激活音频会话。
3.1 系统如何解决竞争的音频需求
当你的应用程序启动时,内置的应用程序(消息、音乐、Safari、手机)可能在后台运行。每一个都可以产生音频:一条短信来了,你10分钟前开始的播客还在继续播放,等等。
如果你把一个设备想象成一个机场,应用程序就像滑行的飞机,这个系统就像一个控制塔。你的应用程序可以发出音频请求并声明它想要的优先级,但是“在停机坪上”发生的事情的最终权威来自系统。使用音频会话与“控制塔”通信。图2-1展示了一个典型的场景—您的应用程序要求在音乐应用程序已经在播放时使用音频。在这个场景中,您的应用程序中断了音乐应用程序。
在图的第1步中,应用程序请求激活它的音频会话。例如,在应用程序启动时,或者在用户轻击音频录制和回放应用程序中的Play按钮时,您可能会发出这样的请求。在步骤2中,系统将考虑激活请求。具体来说,它会考虑您分配给音频会话的类别。在图2-1中,您的应用程序使用了一个类别,该类别需要对其他音频进行消音。
在步骤3和步骤4中,系统关闭音乐应用程序的音频会话,停止其音频播放。最后,在第五步,系统激活你的应用程序的音频会话和播放可以开始。
3.2 激活和关闭您的音频会话
虽然AVFoundation回放和录制类会自动激活音频会话,但手动激活它会让您有机会测试激活是否成功。但是,如果你的应用程序有一个play/pause UI元素,那么编写你的代码,这样用户必须在激活会话之前按play。同样,在更改音频会话的活动/非活动状态时,请检查以确保调用成功。编写代码来优雅地处理系统拒绝激活您的会话。
该系统将关闭您的音频会话,用于时钟或日历闹钟或来电。当用户解除警报或选择忽略电话呼叫时,系统允许您的会话再次激活。是否在中断结束时重新激活会话取决于应用程序类型,如Audio Guidelines By app type所述。
- 激活音频会话的代码如下:
let session = AVAudioSession.sharedInstance()
do {
// 1) Configure your audio session category, options, and mode
// 2) Activate your audio session to enable your custom configuration
try session.setActive(true)
} catch let error as NSError {
print("Unable to activate audio session: \(error.localizedDescription)")
}
若要停用音频会话,请将false传递给setActive方法。
当使用AVFoundation对象(AVPlayer、AVAudioRecorder等)播放或录制音频时,系统负责在中断结束时重新激活音频会话。但是,如果您注册了通知消息并显式地重新激活音频会话,则可以验证重新激活成功,并可以更新应用程序的状态和用户界面。有关更多信息,请参见图3-1。
许多应用程序从来不需要显式地停用它们的音频会话。重要的例外包括VoIP应用程序、逐向导航应用程序,在某些情况下还包括回放和录音应用程序。
- 确保VoIP应用程序的音频会话(通常在后台运行)仅在应用程序处理调用时是活动的。在后台,准备接收电话时,VoIP应用程序的音频会话不应处于激活状态。
- 确保使用录制类别的应用程序的音频会话仅在录制时处于活动状态。在开始录制和停止录制之前,请确保您的会话处于非活动状态,以允许播放其他声音,例如传入消息警报。
- 如果应用程序支持后台音频播放或录制,如果应用程序不积极使用音频(或准备使用音频),则在进入后台时禁用其音频会话。这样做允许系统释放音频资源,以便其他进程可以使用它们。它还可以防止应用程序的音频会话在应用程序进程被操作系统挂起时失效(参见AVAudioSessionInterruptionWasSuspendedKey)。
这个userInfo键只出现在AVAudioSession.InterruptionType中。开始中断事件,其中中断是操作系统挂起应用程序的直接结果。它的关联值是一个布尔NSNumber,其中一个真值表示中断是由于系统挂起应用程序造成的,而不是由另一个音频会话中断的。
3.2.1 Audio Guidelines By app type
3.3 检查其他音频是否正在播放
当你的应用程序激活时,声音可能已经在设备上播放了。例如,当用户启动应用程序时,音乐应用程序可能正在播放歌曲,或者Safari可能正在播放音频流。如果你的应用是一款游戏,了解是否有其他音频在播放尤其重要。许多游戏都有音乐音轨和音效。在iOS人机界面指南中,建议你假设用户在玩游戏时希望其他音频和游戏音效能够继续。
在你的应用委派的applicationDidBecomeActive:方法中,检查音频会话的secondaryAudioShouldBeSilencedHint属性,以确定音频是否已经在播放。当另一个具有非混合音频会话的应用程序正在播放音频时,该值为true。应用程序应该使用这个属性作为使次要于应用程序功能的音频静音的提示。例如,一个使用AVAudioSessionCategoryAmbient的游戏可以使用这个属性来决定是否应该在保持声音效果不静音的情况下静音音轨。
您还可以订阅类型为AVAudioSessionSilenceSecondaryAudioHintNotification的通知,以确保在可选的辅助音频静音应该开始或结束时通知您的应用程序。此通知仅发送给当前处于前台且具有活动音频会话的已注册侦听器。
func setupNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleSecondaryAudio),
name: .AVAudioSessionSilenceSecondaryAudioHint,
object: AVAudioSession.sharedInstance())
}
func handleSecondaryAudio(notification: Notification) {
// Determine hint type
guard let userInfo = notification