iOS - 音频 - AVAudioSession
复制自【https://juejin.im/post/5b3c40bef265da0f774a8a4d】
1. AVAudioSession 概述
最近一年一直在做 IPC Camera 的 iOS 客户端开发。和音频打交道,必须要弄清楚 AVAudioSession。 先看下苹果的官方图:
2. AVAudioSession Category
AVAudioSession 的接口比较简单。APP 启动的时候会自动帮激活 AVAudioSession,当然我们可以手动激活代码如下。
//导入头文件 #import <AVFoundation/AVFoundation.h> //AVAudioSession是一个单例类 AVAudioSession *session = [AVAudioSession sharedInstance]; //AVAudioSessionCategorySoloAmbient是系统默认的category
[session setCategory:AVAudioSessionCategorySoloAmbient error:nil]
; //激活AVAudioSession
[session setActive:YES error:nil]
; 复制代码
可以看到设置 session 这里有两个参数,category 和 options Category iOS 下目前有七种,每种 Category 都对应是否支持下面四种能力
- Interrupts non-mixable apps audio:是否打断不支持混音播放的 APP
- Silenced by the Silent switch:是否会响应手机静音键开关
- Supports audio input:是否支持音频录制
- Supports audio output:是否支持音频播放
下面用图表来直观的看下每种 category 具体的能力集
Category | 是否允许音频播放 / 录音 | 是否打断其他不支持混音 APP | 是否会被静音键或锁屏键静音 |
---|---|---|---|
AVAudioSessionCategoryAmbient | 只支持播放 | 否 | 是 |
AVAudioSessionCategoryAudioProcessing | 不支持播放,不支持录制 | 是 | 否 |
AVAudioSessionCategoryMultiRoute | 支持播放,支持录制 | 是 | 否 |
AVAudioSessionCategoryPlayAndRecord | 支持播放,支持录制 | 默认 YES,可以重写为 NO | 否 |
AVAudioSessionCategoryPlayback | 只支持播放 | 默认 YES,可以重写为 NO | 否 |
AVAudioSessionCategoryRecord | 只支持录制 | 是 | 否(锁屏下仍可录制) |
AVAudioSessionCategorySoloAmbient | 只支持播放 | 是 | 是 |
- AVAudioSessionCategoryAmbient,只支持音频播放。这个 Category,音频会被静音键和锁屏键静音。并且不会打断其他应用的音频播放。
- AVAudioSessionCategorySoloAmbient,这个是系统默认使用的 Category,只支持音频播放。音频会被静音键和锁屏键静音。和 AVAudioSessionCategoryAmbient 不同的是,这个会打断其他应用的音频播放
- AVAudioSessionCategoryPlayback,只支持音频播放。你的音频不会被静音键和锁屏键静音。适用于音频是主要功能的 APP,像网易云这些音乐 app,锁屏后依然可以播放。
需要注意一下,选择支持在静音键切到静音状态以及锁屏键切到锁屏状态下仍然可以播放音频 Category 时,必须在应用中开启支持后台音频功能,详见 UIBackgroundModes。
- AVAudioSessionCategoryRecord,只支持音频录制。不支持播放。
- AVAudioSessionCategoryPlayAndRecord,支持音频播放和录制。音频的输入和输出不需要同步进行,也可以同步进行。需要音频通话类应用,可以使用这个 Category。
- AVAudioSessionCategoryAudioProcessing,只支持本地音频编解码处理。不支持播放和录制。
- AVAudioSessionCategoryMultiRoute,支持音频播放和录制。允许多条音频流的同步输入和输出。(比如 USB 连接外部扬声器输出音频,蓝牙耳机同时播放另一路音频这种特殊需求)
我们也可以通过 AVAudioSession 的属性来读取当前设备支持的 Category
@property(readonly) NSArray<NSString *> *availableCategories; 复制代码
这样可以保证设备兼容性。
设置 Category 的代码示例如下
NSError *setCategoryError = nil; BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&setCategoryError]; if (!success) { //这里可以读取setCategoryError.localizedDescription查看错误原因 } 复制代码
3. AVAudioSession Mode&&Options
刚刚介绍的 Category 定义了七种主场景,实际开发需求中有时候需要对 Category 进行微调整,我们发现这个接口还有两个参数 Mode 和 Options。
/* set session category and mode with options */ - (BOOL)setCategory:(NSString *)category mode:(NSString *)mode options:(AVAudioSessionCategoryOptions)options error:(NSError **)outError API_AVAILABLE(ios(10.0), watchos(3.0), tvos(10.0)); 复制代码
AVAudioSession Mode
我们通过读取下面这条属性获取当前设备支持的 Mode
@property(readonly) NSArray<NSString *> *availableModes; 复制代码
iOS 下有七种 mode 来定制我们的 Category 行为
模式 | 兼容的 Category | 场景 |
---|---|---|
AVAudioSessionModeDefault | All | 默认模式 |
AVAudioSessionModeVoiceChat | AVAudioSessionCategoryPlayAndRecord | VoIP |
AVAudioSessionModeGameChat | AVAudioSessionCategoryPlayAndRecord | 游戏录制,GKVoiceChat 自动设置 |
AVAudioSessionModeVideoRecording | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord | 录制视频 |
AVAudioSessionModeMoviePlayback | AVAudioSessionCategoryPlayback | 视频播放 |
AVAudioSessionModeMeasurement | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback | 最小系统 |
AVAudioSessionModeVideoChat | AVAudioSessionCategoryPlayAndRecord | 视频通话 |
下面逐一介绍下每个 Mode
- AVAudioSessionModeDefault,默认模式, 与所有的 Category 兼容
- AVAudioSessionModeVoiceChat,适用于 VoIP 类型的应用。只能是 AVAudioSessionCategoryPlayAndRecord Category 下。在这个模式系统会自动配置 AVAudioSessionCategoryOptionAllowBluetooth 这个选项。系统会自动选择最佳的内置麦克风组合支持语音聊天。
- AVAudioSessionModeVideoChat,用于视频聊天类型应用,只能是 AVAudioSessionCategoryPlayAndRecord Category 下。适在这个模式系统会自动配置 AVAudioSessionCategoryOptionAllowBluetooth 和 AVAudioSessionCategoryOptionDefaultToSpeaker 选项。系统会自动选择最佳的内置麦克风组合支持视频聊天。
- AVAudioSessionModeGameChat,适用于游戏类应用。使用 GKVoiceChat 对象的应用会自动设置这个模式和 AVAudioSessionCategoryPlayAndRecord Category。实际参数和 AVAudioSessionModeVideoChat 一致
- AVAudioSessionModeVideoRecording,适用于使用摄像头采集视频的应用。只能是 AVAudioSessionCategoryPlayAndRecord 和 AVAudioSessionCategoryRecord 这两个 Category 下。这个模式搭配 AVCaptureSession API 结合来用可以更好地控制音视频的输入输出路径。(例如,设置 automaticallyConfiguresApplicationAudioSession 属性,系统会自动选择最佳输出路径。
- AVAudioSessionModeMeasurement,最小化系统。只用于 AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryRecord、AVAudioSessionCategoryPlayback 这几种 Category。
- AVAudioSessionModeMoviePlayback,适用于播放视频的应用。只用于 AVAudioSessionCategoryPlayback 这个 Category。
AVAudioSession Options
我们还可以使用 options 去微调 Category 行为,如下表
Option | Option 功能说明 | 兼容的 Category |
---|---|---|
AVAudioSessionCategoryOptionMixWithOthers | 支持和其他 APP 音频 mix | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute |
AVAudioSessionCategoryOptionDuckOthers | 系统智能调低其他 APP 音频音量 | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute |
AVAudioSessionCategoryOptionAllowBluetooth | 支持蓝牙音频输入 | AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayAndRecord |
AVAudioSessionCategoryOptionDefaultToSpeaker | 设置默认输出音频到扬声器 | AVAudioSessionCategoryPlayAndRecord |
调优我们的 Category
通过 Category 和合适的 Mode 和 Options 的搭配我们可以调优出我们的效果,下面举两个应用场景:
用过高德地图的都知道,在后台播放 QQ 音乐的时候,如果导航语音出来,QQ 音乐不会停止,而是被智能压低和混音,等导航语音播报完后,QQ 音乐正常播放,这里我们需要后台播放音乐,所以 Category 使用 AVAudioSessionCategoryPlayback,需要混音和智能压低其他 APP 音量,所以 Options 选用 AVAudioSessionCategoryOptionMixWithOthers 和 AVAudioSessionCategoryOptionDuckOthers
代码示例如下
BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDuckOthers error:&setCategoryError]; 复制代码
又或者我希望 AVAudioSessionCategoryPlayAndRecord 这个 Category 默认的音频由扬声器播放,那么可以调用这个接口去调整 Category
- (BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError 复制代码
通过选择合适和 Category,mode 和 options,就可以调优音频的输入输出,来满足日常开发需求(需要注意的是 Category,mode,option 是搭配使用的,而不是简单组合,也就是说某种 Category 支持某些 mode 和 option,从上面的表中也可以看出这一点)
4. 音频中断处理
其他 APP 或者电话会中断我们的 APP 音频,所以相应的我们要做出处理。 我们可以通过监听 AVAudioSessionInterruptionNotification 这个 key 获取音频中断事件
回调回来 Userinfo 有键值
- AVAudioSessionInterruptionTypeKey: 取值 AVAudioSessionInterruptionTypeBegan 表示中断开始 取值 AVAudioSessionInterruptionTypeEnded 表示中断结束
中断开始:我们需要做的是保存好播放状态,上下文,更新用户界面等 中断结束:我们要做的是恢复好状态和上下文,更新用户界面,根据需求准备好之后选择是否激活我们 session。
选择不同的音频播放技术,处理中断方式也有差别,具体如下:
- System Sound Services:使用 System Sound Services 播发音频,系统会自动处理,不受 APP 控制,当中断发生时,音频播放会静音,当中断结束后,音频播放会恢复。
- AV Foundation framework:AVAudioPlayer 类和 AVAudioRecorder 类提供了中断开始和结束的 Delegate 回调方法来处理中断。中断发生,系统会自动停止播放,需要做的是记录播放时间等状态,更新用户界面,等中断结束后,再次调用播放方法,系统会自动激活 session。
- Audio Queue Services, I/O audio unit:使用 aduio unit 这些技术需要处理中断,需要做的是记录播放或者录制的位置,中断结束后自己恢复 audio session。
- OpenAL:使用 OpenAL 播放时,同样需要自己监听中断。管理 OpenAL 上下文,用户中断结束后恢复 audio session。
需要注意的是:1. 有中断开始事件,不一定对应有中断结束事件,所以需要在用户进入前台,点击 UI 操作的时候,需要保存好播放状态和对 Audio Session 管理,以便不影响 APP 的音频功能。2. 音频资源竞争上,一定是电话优先。3. AVAudioSession 同样可以监听外设音频状态,比如耳机拔入拔出。这里不做累述
5. AVAudioSession 总结
AVAudioSession 的作用就是管理音频这一唯一硬件资源的分配,通过调优合适的 AVAudioSession 来适配我们的 APP 对于音频的功能需求。切换音频场景时候,需要相应的切换 AVAudioSession。
发表回复