官方文档
音频是iOS、tvOS和watchOS中的托管服务。系统通过使用音频会话在应用程序、应用程序间和设备级别管理音频行为,它属于一个单独的服务,由操作系统进行调度和管理,音频进程的异常中断并不会导致app的crash. 音频会话的结构如下:
上图中AudioSession
作为app和操作系统的中介,进而充当音视频硬件的中介,通过AudioSession
提供的便利的API完成与操作系统之间的交互。
您可以使用AVAudioSession实例与应用程序的音频会话进行交互,以便:
音频会话管理音频行为
类别表示音频角色
通知支持中断处理:
通知支持音频路由更改处理:
音频会话控制设备配置
音频会话保护用户隐私
音频会话类别是一个键,用于标识应用程序的一组音频行为。通过设置类别,您可以向系统指示您的音频意图,例如当响铃/静音开关被翻转时,您的音频是否应该继续。多个音频会话类别,以及一组覆盖和修改开关,允许您自定义应用程序的音频行为。
每个音频会话类别都为以下每个行为指定一组特定的响应:
Interrupts Nonmixable apps audio
: 是否允许和其它的应用的音频混合Silenced by the Silent switch
: 通过静音开关静音,支持用户通过静音开关关闭app的音屏播放,一般都要支持,不然就是流氓app了。大多数应用程序只需在启动时设置一次类别,但您可以根据需要随时更改类别。您可以在音频会话处于活动状态时更改它;但是,在更改类别或其他会话属性之前,最好先停用音频会话。在禁用会话时进行这些更改可防止不必要的音频系统重新配置。
音频会话默认行为
配置音频会话:
配置音频会话的主要方法是设置其类别。音频会话类别定义了一组音频行为。与每个类别相关联的精确行为不受应用程序控制,而是由操作系统设置。苹果可能会在未来的操作系统版本中改进类别行为,所以你最好的策略是选择最准确地描述你想要的音频行为意图的类别。音频会话类别和模式总结了每个类别的行为详细信息。
// 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)")
}
使用“多路径”类别展开选项
启用背景音频
激活音频会话
设置完音频会话的类别、选项和模式来配置音频会话。要将配置付诸行动,现在需要激活音频会话。
确保VoIP应用程序的音频会话(通常在后台运行)仅在应用程序处理呼叫时处于活动状态。在后台,准备接听电话时,VoIP应用程序的音频会话不应处于活动状态。
确保使用录制类别的应用程序的音频会话仅在录制时处于活动状态。在开始录制之前和停止录制时,请确保会话处于非活动状态,以允许播放其他声音,如传入消息警报。
如果应用程序支持后台音频播放或录制,则在进入后台时,如果应用程序未在使用音频(或正在准备使用音频),请停用其音频会话。这样做可以让系统释放音频资源,以便其他进程可以使用它们。它还可以防止应用程序的音频会话在应用程序进程被操作系统挂起时被停用(请参阅avaudiosessioninterruptionwassupendedkey)。
正在检查是否正在播放其他音频
当你的应用处于活动状态时,设备上可能已经播放了声音
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.userInfo,
let typeValue = userInfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey] as? UInt,
let type = AVAudioSessionSilenceSecondaryAudioHintType(rawValue: typeValue) else {
return
}
if type == .begin {
// Other app audio started playing - mute secondary audio
} else {
// Other app audio stopped playing - restart secondary audio
}
}
中断事件在本例中,FaceTime请求的到达如下所示。编号的步骤与图中的数字相对应
音频中断处理技术
处理来自Siri的中断
AVAudioSessionInterruptionNotification
通知func handleInterruption(_ notification: Notification) {
guard let info = notification.userInfo,
let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
// Interruption began, take appropriate actions (save state, update user interface)
}
else if type == .ended {
guard let optionsValue =
userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else {
return
}
let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended - playback should resume
}
}
}
- userInfo字典可能包含**AVAudioSessionInterruptionOptions**值。**AVAudioSessionInterruptionOptionShouldResume**的选项值是一个提示,指示如果应用程序在被中断时一直在播放,是否应该自动恢复播放。
- 注意: 系统并不能保证开始中断会有相应的结束中断。你的应用程序需要知道一个切换到前台运行状态或用户按下播放按钮。**无论哪种情况,都要确定你的应用程序是否应该重新激活它的音频会话。**
- 响应媒体服务器重置:
- 媒体服务器通过共享服务器进程提供音频和其他多媒体功能。虽然很少见,但在应用程序处于活动状态时,媒体服务器可能会重置。注册**AvaudiosessionMediaServicesResetNotification**通知以监视媒体服务器重置。收到通知后,你的应用程序需要执行以下操作:
- **处理孤立的音频对象(如播放器、录音机、转换器或音频队列)并创建新对**象
- **重置正在跟踪的所有内部音频状态,包括音频会话的所有属性**
- **在适当的时候,使用setActive:错误:方法**
各种音频硬件路由变化
回放的情况类似,但结果不同,如图右侧所示。如果用户在播放过程中拔下耳机插头,你的应用程序应该暂停音频。如果用户在播放过程中插入耳机,你的应用程序应该只允许继续播放。
func setupNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleRouteChange),
name: .AVAudioSessionRouteChange,
object: AVAudioSession.sharedInstance())
}
func handleRouteChange(notification: NSNotification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSessionRouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable:
let session = AVAudioSession.sharedInstance()
for output in session.currentRoute.outputs where output.portType == AVAudioSessionPortHeadphones {
headphonesConnected = true
}
case .oldDeviceUnavailable:
if let previousRoute =
userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
for output in previousRoute.outputs where output.portType == AVAudioSessionPortHeadphones {
headphonesConnected = false
}
}
default: ()
}
}
注意: 音频路由更改也可能导致音频会话的采样率、I/O缓冲区持续时间、通道计数或其他硬件相关值的更改。如果这些值对您的应用程序很重要,请在更改路由后查询它们,以查看它们的值是否已更改。
选择首选音频硬件值
设置首选音频硬件值
let session = AVAudioSession.sharedInstance()
// Configure category and mode
do {
try session.setCategory(AVAudioSessionCategoryRecord, mode: AVAudioSessionModeDefault)
} catch let error as NSError {
print("Unable to set category: (error.localizedDescription)")
}
// Set preferred sample rate
do {
try session.setPreferredSampleRate(44_100)
} catch let error as NSError {
print("Unable to set preferred sample rate: (error.localizedDescription)")
}
// Set preferred I/O buffer duration
do {
try session.setPreferredIOBufferDuration(0.005)
} catch let error as NSError {
print("Unable to set preferred I/O buffer duration: (error.localizedDescription)")
}
// Activate the audio session
do {
try session.setActive(true)
} catch let error as NSError {
print("Unable to activate session. (error.localizedDescription)")
}
// Query the audio session‘s ioBufferDuration and sampleRate properties
// to determine if the preferred values were set
print("Audio Session ioBufferDuration: (session.ioBufferDuration), sampleRate: (session.sampleRate)")
- 选择和配置麦克风:
```
// Preferred Mic = Front, Preferred Polar Pattern = Cardioid
let preferredMicOrientation = AVAudioSessionOrientationFront
let preferredPolarPattern = AVAudioSessionPolarPatternCardioid
// Retrieve your configured and activated audio session
let session = AVAudioSession.sharedInstance()
// Get available inputs
guard let inputs = session.availableInputs else { return }
// Find built-in mic
guard let builtInMic = inputs.first(where: {
$0.portType == AVAudioSessionPortBuiltInMic
}) else { return }
// Find the data source at the specified orientation
guard let dataSource = builtInMic.dataSources?.first (where: {
$0.orientation == preferredMicOrientation
}) else { return }
// Set data source‘s polar pattern
do {
try dataSource.setPreferredPolarPattern(preferredPolarPattern)
} catch let error as NSError {
print("Unable to preferred polar pattern: \(error.localizedDescription)")
}
// Set the data source as the input‘s preferred data source
do {
try builtInMic.setPreferredDataSource(dataSource)
} catch let error as NSError {
print("Unable to preferred dataSource: \(error.localizedDescription)")
}
// Set the built-in mic as the preferred input
// This call will be a no-op if already selected
do {
try session.setPreferredInput(builtInMic)
} catch let error as NSError {
print("Unable to preferred input: \(error.localizedDescription)")
}
// Print Active Configuration
session.currentRoute.inputs.forEach { portDesc in
print("Port: \(portDesc.portType)")
if let ds = portDesc.selectedDataSource {
print("Name: \(ds.dataSourceName)")
print("Polar Pattern: \(ds.selectedPolarPattern ?? "[none]")")
}
}
```
## 权限隐私保护
- 请求和配置权限
```Objective-C
AVAudioSession.sharedInstance().requestRecordPermission { granted in
if granted {
// User granted access. Present recording interface.
} else {
// Present message to user indicating that recording
// can‘t be performed until they change their preference
// under Settings -> Privacy -> Microphone
}
}
```

## 按应用类型划分的音频指南(重要)
- **大多数游戏需要用户交互才能在游戏中发生任何事情**。在设计游戏时使用AVAudioSessionCategoryAmbient或AVAudioSessionCategorySoloAmbient类别。当用户打开另一个应用程序或锁定屏幕时,他们不希望该应用程序继续播放。**通常希望其他应用程序的音频在游戏应用程序播放时继续播放。**
- 以下是一些建议指南:
- 在应用程序代理的`applicationDidBecomeActive:`:方法中激活音频会话。
- 播放应用程序音效,同时允许播放其他应用程序的音频。
- 在其他音频未播放时播放应用程序音轨音频,否则允许播放以前的音频.
- 在结束中断事件后,始终尝试重新激活和恢复音效播放。
- 查询音频会话的**secondaryAudioShouldBeSilencedHint**属性,以确定是否应继续播放游戏的音轨。
- 忽略所有路由更改,除非应用程序特别需要注意它们。
- 在应用程序启动时显示视频启动前设置音频类别。
- 用户控制播放和录制应用程序的音频指南:
- 录音应用和播放应用程序也有类似的指导原则。这些类型的应用程序使用**AVAudioSessionCategoryRecord**、**AVAudioSessionCategoryPlayAndRecord**或**AVAudioSessionCategoryPlayback**类别,**通常在激活它们的音频会话时中断其他系统音频**。用户界面将包括播放/暂停按钮或录制/暂停按钮。
- 以下是一些建议指南:
- 当其他系统的音频会话被激活时,中断其他系统音频。用户界面将包括播放/暂停按钮或录制/暂停按钮。
建议指南:
- 当应用程序进入前台时,等待用户按下播放或录制按钮,然后激活音频会话。
- 当应用程序处于前台时,请保持音频会话处于活动状态,除非它被中断。
- **如果应用程序在转换到后台时没有在活动地播放或录制音频,请停用其音频会话**。这可以**防止它的音频会话被另一个不可混合的应用程序或系统在应用程序被挂起时中断。**
- 更新UI以指示播放或录制在中断时已暂停。不要停用音频会话。
- 观察**AVAudioSessionInterruptionNotification**类型的通知,以通知音频会话中断。**当中断结束时,不要再次开始播放或录制音频,除非应用程序在中断之前已经开始播放或录制音频。**
- 如果**路由更改是由拔出事件引起的,请暂停播放或录制,但保持音频会话处于活动状态。**
- 假设应用程序的音频会话在从挂起状态过渡到前台状态时处于非活动状态。当用户按下播放或录制按钮时重新激活音频会话。
- **确保设置了audio UIBackgroundModes标志**。
- 注册远程控制事件(请参阅**MPRemoteCommandCenter**)并为您的媒体提供适当的正在播放的信息(请参阅**MPNowPlayingInfoCenter**)。锁频播放界面提示
- 使用MPVolumeView对象显示系统卷滑块和路由选择器。
- **使用后台任务而不是流式静默来防止应用程序被挂起。**
- 使用**requestRecordPermission**:方法向用户请求记录输入的权限。不要依赖操作系统来提示用户。
- 对于录制应用程序,请使用**AVAudioSessionCategoryRecord**类别,而不是**AVAudioSessionCategoryRecord**类别。“仅录制”类别实际上会使所有系统输出静音,并且对大多数应用程序来说限制太大。
更多涉及到其它场景的音视频会话配置指南请参照[Audio Guidelines](https://developer.apple.com/library/archive/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/AudioGuidelinesByAppType/AudioGuidelinesByAppType.html#//apple_ref/doc/uid/TP40007875-CH11-SW1)
原文:https://www.cnblogs.com/wwoo/p/audiosession-bian-cheng-zhi-nan.html