一、 AVAudioSession 概述
先看苹果官方图
从上图可以看出AVAudioSession 就是用来管理多个App 对音频硬件设备(麦克风、扬声器)的资源的使用.
举例一下AVAudioSession可以做的事情
- 设置自己的App是否和其他的App音频同时存在,还是中断其他的app声音.
- 在手机调到静音模式下,自己的App音频是否可以播放出声音.
- 电话或者其他App中断自己的App的音频的事件处理.
- 指定音频输入和输出的设备(比如是听筒输出声音,还是扬声器输出声音)
- 是否支持录音,录音同时是否支持音频播放.
二、AVAudioSession Category
AVAudioSession 的接口比较简单,在App启动的时候会自动帮助激活AVAudioSession, 我们也可以手动激活并做一些偏好设置,代码如下:
/**
激活AVAudioSession
*/
-(void)activeAVAudioSession{
// AVAudioSession 是一个单例
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
// AVAudioSessionCategorySoloAmbient 是系统默认的category
[audioSession setCategory:AVAudioSessionCategorySoloAmbient error:nil];
// 激活AVAudioSession
[audioSession setActive:YES error:nil];
}
// 你可以先通过 这个属性来查看当前设备支持的Category 这样可以保证设备的兼容性
// NSArray *categorys = [[AVAudioSession sharedInstance] availableCategories];
// NSLog(@"categorys : %@",categorys);
** AVAudioSessionCategory 就是用来告诉 iOS我们将会采用怎样的方式在系统中使用音频信号, 目前有7种,具体含义如下:**
//1
//此类别适合于“玩”风格的应用程序,例如用户在iPod音频上播放的虚拟钢琴。
//当您使用此类别时,来自其他应用程序的音频与音频混合。
//屏幕锁定和静音开关会打断(关闭)当前音频
AVF_EXPORT NSString *const AVAudioSessionCategoryAmbient;
//2
//当您使用此类别时,来自其他应用程序的音频会被打断(关闭)。
//屏幕锁定和静音开关会打断(关闭)当前音频
AVF_EXPORT NSString *const AVAudioSessionCategorySoloAmbient;
//3
// 当使用该类别时应用程序音频不会因为静音键开关或锁幕而打断。
// 这个类别的设计就是为了防止其他app的音频与当前的app的音频发生混合
AVF_EXPORT NSString *const AVAudioSessionCategoryPlayback;
//4
// 当开始录音时就其它的音频会停止播放音频,当锁屏时会继续录音
AVF_EXPORT NSString *const AVAudioSessionCategoryRecord;
//5
//此类别不会因为锁屏和静音键而中断。
//这个类别适用于同时播放和录音,也适用于录音和回放。
// 如果希望确保应用程序正在录制时不播放诸如Messages警报之类的声音,请使用AVAudioSessionCategoryRecord类别。
AVF_EXPORT NSString *const AVAudioSessionCategoryPlayAndRecord;
//6
// 这个类别会停止掉其它正在播放的音频和正在录制的音频
// 当程序退到后台时正在处理的音频会被停止掉,你需要申请额外的后台处理时间
AVF_EXPORT NSString *const AVAudioSessionCategoryAudioProcessing
//7
//支持音频播放和录制。允许多条音频流的同步输入和输出。
//(比如USB连接外部扬声器输出音频,蓝牙耳机同时播放另一路音频这种特殊需求)
AVF_EXPORT NSString *const AVAudioSessionCategoryMultiRoute
三、AVAudioSession Mode&&Options
我们可以在激活AVAudioSession 时,指定Category是为了告诉系统我们将会以怎样的方式使用音频信息(即我们音频的应用场景是什么),但实际开发过程中只设置Category 可能还不足以瞒住我们的需求,这时我们就可以通过设置AVAudioSession 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));
在iOS 中主要有一下几种 AVAudioSession Mode
//1
// 默认就是这种mode,
//兼容所有的Category
// 在具有多于一个内置麦克风的设备上,使用主麦克风。
AVF_EXPORT NSString *const AVAudioSessionModeDefault NS_AVAILABLE_IOS(5_0);
//2
//主要适用于VoIP 类型的应用(网络电话)
//当使用该模式时设备的音调均衡被优化为语音。
// 注意1: 这种模式需要设置category为 AVAudioSessionCategoryPlayAndRecord
//注意2:在这个模式系统会自动配置AVAudioSessionCategoryOptionAllowBluetooth 这个选项
AVF_EXPORT NSString *const AVAudioSessionModeVoiceChat NS_AVAILABLE_IOS(5_0);
//3
//用于视频聊天类型应用
//只能兼容 AVAudioSessionCategoryPlayAndRecord 。
//系统会自动配置 AVAudioSessionCategoryOptionAllowBluetooth 和
//AVAudioSessionCategoryOptionDefaultToSpeaker 选项。
AVF_EXPORT NSString *const AVAudioSessionModeVideoChat NS_AVAILABLE_IOS(7_0);
//4
//适用于游戏类应用。
//实际参数和AVAudioSessionModeVideoChat一致
AVF_EXPORT NSString *const AVAudioSessionModeGameChat NS_AVAILABLE_IOS(5_0);
//5
// 用于使用摄像头采集视频的应用。
//只能兼容 AVAudioSessionCategoryPlayAndRecord 和 AVAudioSessionCategoryRecord
AVF_EXPORT NSString *const AVAudioSessionModeVideoRecording NS_AVAILABLE_IOS(5_0);
//6
//最小化系统
//只能兼容 AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryRecord
AVF_EXPORT NSString *const AVAudioSessionModeMeasurement NS_AVAILABLE_IOS(5_0);
//7
//适用于播放视频的应用。
//只能兼容AVAudioSessionCategoryPlayback 。
AVF_EXPORT NSString *const AVAudioSessionModeMoviePlayback NS_AVAILABLE_IOS(6_0);
我们还可以使用AVAudioSessionOptions 来微调Category的行为,具体的如下:
//1
// 支持和其它App音频的混合
// 兼容的Category有:
// AVAudioSessionCategoryPlayAndRecord
// AVAudioSessionCategoryPlayback
// AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionMixWithOthers
//2
//系统智能调低其他APP音频音量
// 兼容的Category 有
//AVAudioSessionCategoryPlayAndRecord
//AVAudioSessionCategoryPlayback //AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionDuckOthers
//3
//允许iOS通过蓝牙音频设备来播放我们App的声音。
// 兼容的Category 有
//AVAudioSessionCategoryRecord
//AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionAllowBluetooth
//4
//设置默认输出音频到扬声器
// 兼容的Category 有
//AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionDefaultToSpeaker
四、调优我们的Category
通过给AVAudioSession 设置Category ** 属性,我们告诉系统音频信号将要应用的场景是什么.比如: 当前App音频是否和其它App音频混合, 是否打断其它App的音频,静音或退到后台音频是否被打断, 以及我们是用来播放的还是用来录制的或者播放和录制混合
等等,这些只是个大的方向.我们还可以设置AVAudioSessionMode** 来进一步限制我们的应用场景,比如:我们主要应用场景是网络电话,是语音聊天,是游戏类型吗,还是一般的通用应用场景
等等,甚至我们还可以通过设置AVAudioSessionOptions参数告诉系统更详细的应用场景,比如: 是否支持蓝牙输出, 是否默认扬声器输出,是否在播放时降低其他音频的声音,是否允许和其他音频混合
等等.
通过Category和合适的Mode和Options的搭配我们可以调优出我们的效果,下面举两个应用场景:
- 用过高德地图的都知道,在后台播放QQ音乐的时候,如果导航语音出来,QQ音乐不会停止,而是被智能压低和混音,等导航语音播报完后,QQ音乐正常播放
(1)这里我们需要后台播放音乐,所以Category使用AVAudioSessionCategoryPlayback
(2)需要混音和智能压低其他APP音量,所以Options选用 AVAudioSessionCategoryOptionMixWithOthers和AVAudioSessionCategoryOptionDuckOthers**
代码示例如下:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = nil;
[audioSession setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers |
AVAudioSessionCategoryOptionDuckOthers
error:&err];
通过选择合适和Category,mode和options,就可以调优音频的输入输出,来满足日常开发需求 (需要注意的是Category,mode,option是搭配使用的,而不是简单组合,也就是说某种Category支持某些mode和option,从上面可以看出这一点)
五、音频中断处理
- AVAudioSessionInterruptionTypeKey:
其他App 或电话会中断我们的App 音频,所以相应的我们要做出对应的处理.
我们可以通过监听AVAudioSessionInterruptionNotification
获取中断事件.
回调回来的UserInfo 有键值:
AVAudioSessionInterruptionTypeBegan
表示中断开始
AVAudioSessionInterruptionTypeEnded
表示中断结束
中断开始: 我们需要做的是保存好播放状态, 上下文, 更新用户的界面
中断结束: 我们要做的是恢复好状态和上下文,更新用户界面, 根据需求准备好之后选择是否激活我们的Session
选择不同的音频播放技术,处理中断的方式也有差别,具体如下:
-
System Sound Services: 使用System Sound Services 播放音频,系统会自动处理,不受App控制,当中断发生时,音频播放会静音,当中断结束后,音频播放会恢复.
-
AVFoundation Framework: AVAudioPlayer 类和 AVAudioRecorder 类提供了中断开始和结束的Delegate 回调方法来处理中断.
(1) 中断发生, 系统会自动停止播放,需要做的是记录播放时间等状态,更新用户界面
(2)中断结束后,再次调用播放方法,系统会自动激活session. -
OpenAL : 使用OpenAL 播放时,同样需要自己监听中断.管理OpenAL上下文,中户中断结束后恢复audio session
需要注意的是:
1.有中断开始事件,但不一定有对应的中断结束事件,所以需要在用户进入前台,点击UI操作时,需要保存好播放状态和对应的Audio Session管理,以便不影响App 的音频功能.
2. 音频资源的竞争上,一定是电话优先.
3. AVAudionSession同样可以监听外设音频状态,比如耳机拔出和插入状态.
六、Audio Route (音频路由的选择)
当你的iphone 接有多个外接音频设备时(耳机, 蓝牙耳机等), AudioSession 遵循 last-in wins 的原则来选择外接设备.即 声音将会被导入最后接入的设备.
当没有介接入任何音频设备时, 一般情况下声音会默认从扬声器出来,但有一个例外的情况在playAndRecord这个Category下,听筒会成为默认的输出设备 如果你想要改变这个行为,你可以提供MPVolumeView来让用户切换到扬声器,也可以通过 overrideOutputAudioPort 方法来programmingly 切换到扬声器,也可以修改categoryOption为AVAudioSessionCategoryOptionDefaultToSpeaker
-
playAndRecord 下切换到扬声器
除了让用户手动切换,你也可以通过以下2种方式在程序里面切换
(1) 修改AVAudioSession 的category 方式[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
(2)OverrideOutputAudioPort:
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
关于这两种方式切换扬声器的方法的说明:
(1) 使用AVAudioSessionPortOverride 方式要更为快捷一些
(2) 使用这2中方式切换扬声器的前提是 category 被设置为AVAudioSessionCategoryPlayAndRecord
(3) 上面2种方式只是临时的切换,任何路由改变或中断将导致音频被路由回其正常路径,遵循last-in wins 的规则。
七、 总结
AVAudioSession 的作用就是管理音频这一唯一的的硬件资源分配,通过调优合适的AVAudioSession来适配我们的App对音频功能需求.切换音频场景时,需要相应的切换AVAudioSession
参考文章1 iOS 音频-AVAudioSession
参考文章2 (AudioSession详解 Category选择)
参考文章3《AV Foundation 开发秘籍》 也不错
参考文章4 苹果官方文档
参考文章5 AVAudioSession bluetooth